簡書 占小狼,轉(zhuǎn)載請注明原創(chuàng)出處,謝謝!
為了滿足高性能、低延遲的要求,大部分應(yīng)用都采用CMS算法作為服務(wù)端的垃圾收集器,而在HotSpot的實(shí)現(xiàn)中,針對CMS算法,對于一些參數(shù)進(jìn)行了一系列的重新定制,所以在使用-XX+:UseConcMarkSweepGC時,下面幾點(diǎn)是需要注意的。
-Xmx2g
-Xms2g
-XX:+UseConcMarkSweepGC
假設(shè)使用上述參數(shù),只設(shè)置了堆的大小,并且使用了CMS,應(yīng)用啟動后,堆的相關(guān)默認(rèn)參數(shù)如下:

新生代的初始和最大容量為332.75M,為什么是這個數(shù)字?一切疑團(tuán)要從源碼解開。
因?yàn)樵O(shè)置了UseConcMarkSweepGC,在JVM啟動的時候,會執(zhí)行如下邏輯進(jìn)行一些特殊設(shè)置,位于Arguments::set_cms_and_parnew_gc_flags().
1、UseParNewGC
如果UseParNewGC為默認(rèn)值false,則會設(shè)置為true,所以不需要顯示的-XX:+UseParNewGC.
2、ParallelGCThreads
如果沒有設(shè)置ParallelGCThreads,默認(rèn)為0,會根據(jù)cpu數(shù)量重新計(jì)算,大概算法如下:
int ncpus = (unsigned int) os::active_processor_count();
ParallelGCThreads = (ncpus <= 8) ? ncpus : (8 + ((ncpus - 8) * 5) / 8);
如果是單核cpu的話,那UseParNewGC會被重新設(shè)置為false,ParallelGCThreads也會被重新設(shè)置為0
3、NewSize and MaxNewSize
新生代合適的最大值計(jì)算邏輯如下:
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,新生代默認(rèn)的最大值是max_heap/(NewRatio+1),即最大堆的1/3,這里當(dāng)然需要重新計(jì)算,其中young_gen_per_worker默認(rèn)是64M,parallel_gc_threads為4(本機(jī)只有4個cpu),ScaleForWordSize函數(shù)實(shí)現(xiàn)如下:
#define ScaleForWordSize(x) align_size_down_((x) * 13 / 10, HeapWordSize)
計(jì)算出來332.8M,因?yàn)橐M(jìn)行地址對齊,所以和圖中所示差不多(332.75M)
如果沒有設(shè)置NewSize,NewSize和MaxNewSize都會設(shè)置成preferred_max_new_size,否則MaxNewSize會被賦值一個較大值(NewSize和preferred_max_new_size較大值)
如果沒有設(shè)置OldSize,OldSize一般被設(shè)置成NewSize的兩倍(本來默認(rèn)就是2倍的關(guān)系),具體算法是:
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);
}
}
}
其實(shí),整個過程還是蠻繞的。
4、MaxTenuringThreshold
MaxTenuringThreshold是對象晉升到老年代的最大年齡,默認(rèn)是15,但是在CMS的情況下,可能會被設(shè)置成一個更小的值6
if (FLAG_IS_DEFAULT(MaxTenuringThreshold) &&
FLAG_IS_DEFAULT(SurvivorRatio)) {
FLAG_SET_ERGO(intx, MaxTenuringThreshold, tenuring_default);
}
前提:沒有改變MaxTenuringThreshold(15)和SurvivorRatio(8)的默認(rèn)值
其它和CMS有關(guān)的情況:
5、ExplicitGCInvokesConcurrent
使用該參數(shù),可以避免System.gc()的FGC,使用并發(fā)的CMS進(jìn)行垃圾回收
6、GCLockerInvokesConcurrent
慎用,具體情況可以看看《慎重!是否使用GCLockerInvokesConcurrent》?
7、concurrent mode failed
只有CMS算法中才會出現(xiàn)concurrent mode failed,一旦它的出現(xiàn),意味著有一次耗時很長的FGC了,更具體的可以查看《關(guān)于CMS垃圾收集算法的一些疑惑》