Poison

jmap

源码位于 JMap.java at jdk8-b120,可以看到对参数的解析以及对实际命令的调用。如果是堆转储会调用至 attachListener.cpp at jdk8-b120:

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
// Implementation of "dumpheap" command.
// See also: HeapDumpDCmd class
//
// Input arguments :-
// arg0: Name of the dump file
// arg1: "-live" or "-all"
jint dump_heap(AttachOperation* op, outputStream* out) {
const char* path = op->arg(0);
if (path == NULL || path[0] == '\0') {
out->print_cr("No dump file specified");
} else {
bool live_objects_only = true; // default is true to retain the behavior before this change is made
const char* arg1 = op->arg(1);
if (arg1 != NULL && (strlen(arg1) > 0)) {
if (strcmp(arg1, "-all") != 0 && strcmp(arg1, "-live") != 0) {
out->print_cr("Invalid argument to dumpheap operation: %s", arg1);
return JNI_ERR;
}
live_objects_only = strcmp(arg1, "-live") == 0;
}

// Request a full GC before heap dump if live_objects_only = true
// This helps reduces the amount of unreachable objects in the dump
// and makes it easier to browse.
HeapDumper dumper(live_objects_only /* request GC */);
int res = dumper.dump(op->arg(0));
if (res == 0) {
out->print_cr("Heap dump file created");
} else {
// heap dump failed
ResourceMark rm;
char* error = dumper.error_as_C_string();
if (error == NULL) {
out->print_cr("Dump failed - reason unknown");
} else {
out->print_cr("%s", error);
}
}
}
return JNI_OK;
}

可以看出如果指定了 live 选项,则会在堆转储前执行一次 Full GC 以降低堆转储的大小,便于分析。在 jmap 中也有提到:

The live suboption is optional, but when specified, only the active objects in the heap are dumped.

值得注意的是在堆转储文件中的 int[] 部分来源于 GC、TLAB 等填充的内存空间,如 collectedHeap.cpp at jdk8-b120 中的 fill_with_object 方法:

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
void CollectedHeap::fill_with_object(HeapWord* start, size_t words, bool zap)
{
DEBUG_ONLY(fill_args_check(start, words);)
HandleMark hm; // Free handles before leaving.
fill_with_object_impl(start, words, zap);
}

void
CollectedHeap::fill_with_object_impl(HeapWord* start, size_t words, bool zap)
{
assert(words <= filler_array_max_size(), "too big for a single object");

if (words >= filler_array_min_size()) {
fill_with_array(start, words, zap);
} else if (words > 0) {
assert(words == min_fill_size(), "unaligned size");
post_allocation_setup_common(SystemDictionary::Object_klass(), start);
}
}

void
CollectedHeap::fill_with_array(HeapWord* start, size_t words, bool zap)
{
assert(words >= filler_array_min_size(), "too small for an array");
assert(words <= filler_array_max_size(), "too big for a single object");

const size_t payload_size = words - filler_array_hdr_size();
const size_t len = payload_size * HeapWordSize / sizeof(jint);
assert((int)len >= 0, err_msg("size too large " SIZE_FORMAT " becomes %d", words, (int)len));

// Set the length first for concurrent GC.
((arrayOop)start)->set_length((int)len);
post_allocation_setup_common(Universe::intArrayKlassObj(), start);
DEBUG_ONLY(zap_filler_array(start, words, zap);)
}

fill_with_object 方法被不少 GC 收集器调用,也被 ThreadLocalAllocBuffer 调用,如 threadLocalAllocBuffer.cpp at jdk8-b120:

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
// Fills the current tlab with a dummy filler array to create
// an illusion of a contiguous Eden and optionally retires the tlab.
// Waste accounting should be done in caller as appropriate; see,
// for example, clear_before_allocation().
void ThreadLocalAllocBuffer::make_parsable(bool retire) {
if (end() != NULL) {
invariants();

if (retire) {
myThread()->incr_allocated_bytes(used_bytes());
}

CollectedHeap::fill_with_object(top(), hard_end(), retire);

if (retire || ZeroTLAB) { // "Reset" the TLAB
set_start(NULL);
set_top(NULL);
set_pf_top(NULL);
set_end(NULL);
}
}
assert(!(retire || ZeroTLAB) ||
(start() == NULL && end() == NULL && top() == NULL),
"TLAB must be reset");
}
Reference

jdk/HeapHprofBinWriter.java at jdk8-b120 · openjdk/jdk · GitHub
Can jmap -histo trigger full garbage collection? - Stack Overflow