gclog 发现young gc频率有点频繁
jmap查看内存对象,未发现内存泄漏行为,major gc后堆内存大小大部分被清理干净。
因此怀疑是年轻代设置不合理,导致应该在young gc阶段被清理的对象,晋升到年老代
jmap -heap pid 查看堆内存大小
发现年轻代过小,与印象中的默认年轻代:年老代=1:2,有区别
查资料了解到,针对不同的垃圾回收器JVM会进行一些参数优化,我们使用的是CMS垃圾回收器,源码链接:
https://github.com/AdoptOpenJDK/openjdk-jdk8u/blob/master/hotspot/src/share/vm/runtime/arguments.cpp
当不手动配置MaxNewSize和NewRatio,年轻代取值如下
// If either MaxNewSize or NewRatio is set on the command line,
// assume the user is trying to set the size of the young gen.
if (FLAG_IS_DEFAULT(MaxNewSize) && FLAG_IS_DEFAULT(NewRatio)) {
// Set MaxNewSize to our calculated preferred_max_new_size unless
// NewSize was set on the command line and it is larger than
// preferred_max_new_size.
if (!FLAG_IS_DEFAULT(NewSize)) { // NewSize explicitly set at command-line
FLAG_SET_ERGO(uintx, MaxNewSize, MAX2(NewSize, preferred_max_new_size));
} else {
FLAG_SET_ERGO(uintx, MaxNewSize, preferred_max_new_size);
}
if (PrintGCDetails && Verbose) {
// Too early to use gclog_or_tty
tty->print_cr("CMS ergo set MaxNewSize: " SIZE_FORMAT, MaxNewSize);
}
// Code along this path potentially sets NewSize and OldSize
if (PrintGCDetails && Verbose) {
// Too early to use gclog_or_tty
tty->print_cr("CMS set min_heap_size: " SIZE_FORMAT
" initial_heap_size: " SIZE_FORMAT
" max_heap: " SIZE_FORMAT,
min_heap_size(), InitialHeapSize, max_heap);
}
size_t min_new = preferred_max_new_size;
if (FLAG_IS_CMDLINE(NewSize)) {
min_new = NewSize;
}
if (max_heap > min_new && min_heap_size() > min_new) {
// Unless explicitly requested otherwise, make young gen
// at least min_new, and at most preferred_max_new_size.
if (FLAG_IS_DEFAULT(NewSize)) {
FLAG_SET_ERGO(uintx, NewSize, MAX2(NewSize, min_new));
FLAG_SET_ERGO(uintx, NewSize, MIN2(preferred_max_new_size, NewSize));
if (PrintGCDetails && Verbose) {
// Too early to use gclog_or_tty
tty->print_cr("CMS ergo set NewSize: " SIZE_FORMAT, NewSize);
}
}
// Unless explicitly requested otherwise, size old gen
// so it's NewRatio x of NewSize.
if (FLAG_IS_DEFAULT(OldSize)) {
if (max_heap > NewSize) {
FLAG_SET_ERGO(uintx, OldSize, MIN2(NewRatio*NewSize, max_heap - NewSize));
if (PrintGCDetails && Verbose) {
// Too early to use gclog_or_tty
tty->print_cr("CMS ergo set OldSize: " SIZE_FORMAT, OldSize);
}
}
}
}
}
preferred_max_new_size是根据年轻代比例和 gc线程数算出两个大小,取最小值。
MIN2(max_heap/(NewRatio+1), ScaleForWordSize(young_gen_per_worker * parallel_gc_threads))
gc线程数默认当cpu小于等于8时,取的cpu核心数
计算可知:64M(硬件相关) * 2(核心相关) * 13 / 10 = 166.4,再做一下字节对齐就等于166.375M,所以我们2c8g的年轻代大小就不太合理,修改完成后Major GC频率降低至约每天一次
备注:该优化会将对象从年轻代升到年老代历经的回收次数修改为6,源码如下
// Now make adjustments for CMS
intx tenuring_default = (intx)6;
...
// Unless explicitly requested otherwise, definitely
// promote all objects surviving "tenuring_default" scavenges.
if (FLAG_IS_DEFAULT(MaxTenuringThreshold) &&
FLAG_IS_DEFAULT(SurvivorRatio)) {
FLAG_SET_ERGO(uintx, MaxTenuringThreshold, tenuring_default);
}