1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_SERVICES_MEM_TRACKER_HPP
#define SHARE_VM_SERVICES_MEM_TRACKER_HPP
#include "memory/allocation.hpp"
#include "runtime/globals.hpp"
#include "runtime/mutex.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.hpp"
#include "services/memPtr.hpp"
#include "services/memRecorder.hpp"
#include "services/memSnapshot.hpp"
#include "services/memTrackWorker.hpp"
#ifdef SOLARIS
#include "thread_solaris.inline.hpp"
#endif
#ifdef _DEBUG_
#define DEBUG_CALLER_PC os::get_caller_pc(3)
#else
#define DEBUG_CALLER_PC 0
#endif
// The thread closure walks threads to collect per-thread
// memory recorders at NMT sync point
class SyncThreadRecorderClosure : public ThreadClosure {
private:
int _thread_count;
public:
SyncThreadRecorderClosure() {
_thread_count =0;
}
void do_thread(Thread* thread);
int get_thread_count() const {
return _thread_count;
}
};
class BaselineOutputer;
class MemSnapshot;
class MemTrackWorker;
class Thread;
/*
* MemTracker is the 'gate' class to native memory tracking runtime.
*/
class MemTracker : AllStatic {
friend class MemTrackWorker;
friend class MemSnapshot;
friend class SyncThreadRecorderClosure;
// NMT state
enum NMTStates {
NMT_uninited, // not yet initialized
NMT_bootstrapping_single_thread, // bootstrapping, VM is in single thread mode
NMT_bootstrapping_multi_thread, // bootstrapping, VM is about to enter multi-thread mode
NMT_started, // NMT fully started
NMT_shutdown_pending, // shutdown pending
NMT_final_shutdown, // in final phase of shutdown
NMT_shutdown // shutdown
};
// native memory tracking level
enum NMTLevel {
NMT_off, // native memory tracking is off
NMT_summary, // don't track callsite
NMT_detail // track callsite also
};
public:
enum ShutdownReason {
NMT_shutdown_none, // no shutdown requested
NMT_shutdown_user, // user requested shutdown
NMT_normal, // normal shutdown, process exit
NMT_out_of_memory, // shutdown due to out of memory
NMT_initialization, // shutdown due to initialization failure
NMT_use_malloc_only, // can not combine NMT with UseMallocOnly flag
NMT_error_reporting, // shutdown by vmError::report_and_die()
NMT_out_of_generation, // running out of generation queue
NMT_sequence_overflow // overflow the sequence number
};
public:
// initialize NMT tracking level from command line options, called
// from VM command line parsing code
static void init_tracking_options(const char* option_line);
// if NMT is enabled to record memory activities
static inline bool is_on() {
return (_tracking_level >= NMT_summary &&
_state >= NMT_bootstrapping_single_thread);
}
// user readable reason for shutting down NMT
static const char* reason() {
switch(_reason) {
case NMT_shutdown_none:
return "Native memory tracking is not enabled";
case NMT_shutdown_user:
return "Native memory tracking has been shutdown by user";
case NMT_normal:
return "Native memory tracking has been shutdown due to process exiting";
case NMT_out_of_memory:
return "Native memory tracking has been shutdown due to out of native memory";
case NMT_initialization:
return "Native memory tracking failed to initialize";
case NMT_error_reporting:
return "Native memory tracking has been shutdown due to error reporting";
case NMT_out_of_generation:
return "Native memory tracking has been shutdown due to running out of generation buffer";
case NMT_sequence_overflow:
return "Native memory tracking has been shutdown due to overflow the sequence number";
case NMT_use_malloc_only:
return "Native memory tracking is not supported when UseMallocOnly is on";
default:
ShouldNotReachHere();
return NULL;
}
}
// test if we can walk native stack
static bool can_walk_stack() {
// native stack is not walkable during bootstrapping on sparc
#if defined(SPARC)
return (_state == NMT_started);
#else
return (_state >= NMT_bootstrapping_single_thread && _state <= NMT_started);
#endif
}
// if native memory tracking tracks callsite
static inline bool track_callsite() { return _tracking_level == NMT_detail; }
// shutdown native memory tracking capability. Native memory tracking
// can be shutdown by VM when it encounters low memory scenarios.
// Memory tracker should gracefully shutdown itself, and preserve the
// latest memory statistics for post morten diagnosis.
static void shutdown(ShutdownReason reason);
// if there is shutdown requested
static inline bool shutdown_in_progress() {
return (_state >= NMT_shutdown_pending);
}
// bootstrap native memory tracking, so it can start to collect raw data
// before worker thread can start
// the first phase of bootstrapping, when VM still in single-threaded mode
static void bootstrap_single_thread();
// the second phase of bootstrapping, VM is about or already in multi-threaded mode
static void bootstrap_multi_thread();
// start() has to be called when VM still in single thread mode, but after
// command line option parsing is done.
static void start();
// record a 'malloc' call
static inline void record_malloc(address addr, size_t size, MEMFLAGS flags,
address pc = 0, Thread* thread = NULL) {
assert(is_on(), "check by caller");
if (NMT_CAN_TRACK(flags)) {
create_memory_record(addr, (flags|MemPointerRecord::malloc_tag()), size, pc, thread);
}
}
// record a 'free' call
static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) {
if (is_on() && NMT_CAN_TRACK(flags)) {
create_memory_record(addr, MemPointerRecord::free_tag(), 0, 0, thread);
}
}
// record a 'realloc' call
static inline void record_realloc(address old_addr, address new_addr, size_t size,
MEMFLAGS flags, address pc = 0, Thread* thread = NULL) {
if (is_on()) {
record_free(old_addr, flags, thread);
record_malloc(new_addr, size, flags, pc, thread);
}
}
// record arena size
static inline void record_arena_size(address addr, size_t size) {
// we add a positive offset to arena address, so we can have arena size record
// sorted after arena record
if (is_on() && !UseMallocOnly) {
create_memory_record((addr + sizeof(void*)), MemPointerRecord::arena_size_tag(), size,
0, NULL);
}
}
// record a virtual memory 'reserve' call
static inline void record_virtual_memory_reserve(address addr, size_t size,
address pc = 0, Thread* thread = NULL) {
if (is_on()) {
assert(size > 0, "reserve szero size");
create_memory_record(addr, MemPointerRecord::virtual_memory_reserve_tag(),
size, pc, thread);
}
}
// record a virtual memory 'commit' call
static inline void record_virtual_memory_commit(address addr, size_t size,
address pc = 0, Thread* thread = NULL) {
if (is_on()) {
create_memory_record(addr, MemPointerRecord::virtual_memory_commit_tag(),
size, pc, thread);
}
}
// record a virtual memory 'uncommit' call
static inline void record_virtual_memory_uncommit(address addr, size_t size,
Thread* thread = NULL) {
if (is_on()) {
create_memory_record(addr, MemPointerRecord::virtual_memory_uncommit_tag(),
size, 0, thread);
}
}
// record a virtual memory 'release' call
static inline void record_virtual_memory_release(address addr, size_t size,
Thread* thread = NULL) {
if (is_on()) {
create_memory_record(addr, MemPointerRecord::virtual_memory_release_tag(),
size, 0, thread);
}
}
// record memory type on virtual memory base address
static inline void record_virtual_memory_type(address base, MEMFLAGS flags,
Thread* thread = NULL) {
if (is_on()) {
assert(base > 0, "wrong base address");
assert((flags & (~mt_masks)) == 0, "memory type only");
create_memory_record(base, (flags | MemPointerRecord::virtual_memory_type_tag()),
0, 0, thread);
}
}
// create memory baseline of current memory snapshot
static bool baseline();
// is there a memory baseline
static bool has_baseline() {
return _baseline.baselined();
}
// print memory usage from current snapshot
static bool print_memory_usage(BaselineOutputer& out, size_t unit,
bool summary_only = true);
// compare memory usage between current snapshot and baseline
static bool compare_memory_usage(BaselineOutputer& out, size_t unit,
bool summary_only = true);
// sync is called within global safepoint to synchronize nmt data
static void sync();
// called when a thread is about to exit
static void thread_exiting(JavaThread* thread);
// retrieve global snapshot
static MemSnapshot* get_snapshot() {
assert(is_on(), "native memory tracking is off");
if (shutdown_in_progress()) {
return NULL;
}
return _snapshot;
}
// print tracker stats
NOT_PRODUCT(static void print_tracker_stats(outputStream* st);)
NOT_PRODUCT(static void walk_stack(int toSkip, char* buf, int len);)
private:
// start native memory tracking worker thread
static bool start_worker();
// called by worker thread to complete shutdown process
static void final_shutdown();
protected:
// retrieve per-thread recorder of the specified thread.
// if the recorder is full, it will be enqueued to overflow
// queue, a new recorder is acquired from recorder pool or a
// new instance is created.
// when thread == NULL, it means global recorder
static MemRecorder* get_thread_recorder(JavaThread* thread);
// per-thread recorder pool
static void release_thread_recorder(MemRecorder* rec);
static void delete_all_pooled_recorders();
// pending recorder queue. Recorders are queued to pending queue
// when they are overflowed or collected at nmt sync point.
static void enqueue_pending_recorder(MemRecorder* rec);
static MemRecorder* get_pending_recorders();
static void delete_all_pending_recorders();
private:
// retrieve a pooled memory record or create new one if there is not
// one available
static MemRecorder* get_new_or_pooled_instance();
static void create_memory_record(address addr, MEMFLAGS type,
size_t size, address pc, Thread* thread);
static void create_record_in_recorder(address addr, MEMFLAGS type,
size_t size, address pc, JavaThread* thread);
private:
// global memory snapshot
static MemSnapshot* _snapshot;
// a memory baseline of snapshot
static MemBaseline _baseline;
// query lock
static Mutex* _query_lock;
// a thread can start to allocate memory before it is attached
// to VM 'Thread', those memory activities are recorded here.
// ThreadCritical is required to guard this global recorder.
static MemRecorder* _global_recorder;
// main thread id
debug_only(static intx _main_thread_tid;)
// pending recorders to be merged
static volatile MemRecorder* _merge_pending_queue;
NOT_PRODUCT(static volatile jint _pending_recorder_count;)
// pooled memory recorders
static volatile MemRecorder* _pooled_recorders;
// memory recorder pool management, uses following
// counter to determine if a released memory recorder
// should be pooled
// latest thread count
static int _thread_count;
// pooled recorder count
static volatile jint _pooled_recorder_count;
// worker thread to merge pending recorders into snapshot
static MemTrackWorker* _worker_thread;
// how many safepoints we skipped without entering sync point
static int _sync_point_skip_count;
// if the tracker is properly intialized
static bool _is_tracker_ready;
// tracking level (off, summary and detail)
static enum NMTLevel _tracking_level;
// current nmt state
static volatile enum NMTStates _state;
// the reason for shutting down nmt
static enum ShutdownReason _reason;
};
#endif // SHARE_VM_SERVICES_MEM_TRACKER_HPP
|