占小狼,转载请注明原创出处,谢谢!
为了满足高性能、低延迟的要求,大部分应用都采用CMS算法作为服务端的垃圾收集器,而在HotSpot的实现中,针对CMS算法,对于一些参数进行了一系列的重新定制,所以在使用-XX+:UseConcMarkSweepGC
时,下面几点是需要注意的。
-Xmx2g
-Xms2g
-XX:+UseConcMarkSweepGC
假设使用上述参数,只设置了堆的大小,并且使用了CMS,应用启动后,堆的相关默认参数如下:
新生代的初始和最大容量为332.75M,为什么是这个数字?一切疑团要从源码解开。
因为设置了UseConcMarkSweepGC
,在JVM启动的时候,会执行如下逻辑进行一些特殊设置,位于Arguments::set_cms_and_parnew_gc_flags()
.
1、UseParNewGC
如果UseParNewGC
为默认值false,则会设置为true,所以不需要显示的-XX:+UseParNewGC
.
2、ParallelGCThreads
如果没有设置ParallelGCThreads,默认为0,会根据cpu数量重新计算,大概算法如下:
int ncpus = (unsigned int) os::active_processor_count();
ParallelGCThreads = (ncpus <= 8) ? ncpus : (8 + ((ncpus - 8) * 5) / 8);
如果是单核cpu的话,那UseParNewGC
会被重新设置为false,ParallelGCThreads也会被重新设置为0
3、NewSize and MaxNewSize
新生代合适的最大值计算逻辑如下:
const size_t preferred_max_new_size_unaligned =
MIN2(max_heap/(NewRatio+1), ScaleForWordSize(young_gen_per_worker * parallel_gc_threads));
size_t preferred_max_new_size =
align_size_up(preferred_max_new_size_unaligned, os::vm_page_size());
如果不是CMS,新生代默认的最大值是max_heap/(NewRatio+1)
,即最大堆的1/3,这里当然需要重新计算,其中young_gen_per_worker
默认是64M,parallel_gc_threads
为4(本机只有4个cpu),ScaleForWordSize
函数实现如下:
#define ScaleForWordSize(x) align_size_down_((x) * 13 / 10, HeapWordSize)
计算出来332.8M,因为要进行地址对齐,所以和图中所示差不多(332.75M)
如果没有设置NewSize,NewSize和MaxNewSize都会设置成preferred_max_new_size
,否则MaxNewSize会被赋值一个较大值(NewSize和preferred_max_new_size
较大值)
如果没有设置OldSize,OldSize一般被设置成NewSize的两倍(本来默认就是2倍的关系),具体算法是:
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);
}
}
}
其实,整个过程还是蛮绕的。
4、MaxTenuringThreshold
MaxTenuringThreshold
是对象晋升到老年代的最大年龄,默认是15,但是在CMS的情况下,可能会被设置成一个更小的值6
if (FLAG_IS_DEFAULT(MaxTenuringThreshold) &&
FLAG_IS_DEFAULT(SurvivorRatio)) {
FLAG_SET_ERGO(intx, MaxTenuringThreshold, tenuring_default);
}
前提:没有改变MaxTenuringThreshold(15)和SurvivorRatio(8)的默认值
其它和CMS有关的情况:
5、ExplicitGCInvokesConcurrent
使用该参数,可以避免System.gc()
的FGC,使用并发的CMS进行垃圾回收
6、GCLockerInvokesConcurrent
慎用,具体情况可以看看《慎重!是否使用GCLockerInvokesConcurrent》�
7、concurrent mode failed
只有CMS算法中才会出现concurrent mode failed
,一旦它的出现,意味着有一次耗时很长的FGC了,更具体的可以查看《关于CMS垃圾收集算法的一些疑惑》