虽然CMS在JDK9已经被淘汰了,但是我们现在依然使用的CMS收集器。
-XX:PretenureSizeThreshold
先说下这个参数的作用。默认值是0,0表示对象不管多大都优先在Eden区分配,如果不为0,大于这个值的对象将会分配置直接分配到老年代。这个参数只对ParNew和Serial收集器起作用。
对于大对象直接分配到老年代实际上是有两种情况:
- 在新生代分配失败的不持有任何对象大数组,直接分配到老年代。
- 大于
-XX:PretenureSizeThreshold
参数的对象。
CMS的使用场景
适用于GC停顿时间短的场.
缺点
因为CMS使用的标记-清除算法,当程序运行的时间长了,会产生内存碎片,导致触发FullGC.
CMS触发GC的时机
- 主动触发
-
CMS默认每隔两秒会进行一次检查,判断老年代使用率是否达到阈值,需要进行OldGC。
-XX:CMSInitiatingOccupancyFraction
:老年代使用阈值默认是92%。-XX:+UseCMSInitiatingOccupancyOnly
:始终基于设定的阈值,不根据运行情况进行调整。如果没有 -XX:+UseCMSInitiatingOccupancyOnly 这个参数, 只有第一次会使用CMSInitiatingPermOccupancyFraction=92 这个值. 后面的情况会自动调整。 -
晋升分配担保失败,触发GC。
- 被动触发
- YGC过程发生Promotion Failed,进而对老年代进行回收。
- System.gc()
被动触发需要对老年代进行压缩整理,由Serial Old处理,需要STW.
GC过程
- 初始标记:标记GCroots可以直达的对象。(STW)
- 并发标记:引用发生变化的对象所在的Card(这个Card不是Card Table)标记为Dirty
- 并发预处理:处理新生代对象对老年代的引用,重新扫描Dirty,清除Card标记。
- 可中止的并发预处理:处理Dirty,更新新生代对象对老年代的引用的情况,主要是等待一次YGC,这样对于重新标记阶段将会减轻负担
- 重新标记:增量更新,处理跨代引用。(STW)
- 并发回收:
- 重置资源
CMS性能调优
CMS垃圾收集器可能造成停顿的点
- YGC
- 初始标记阶段
- 重新标记阶段
- Concurrent mode failure导致使用Serial-old收集老年代停顿。
- Promotion Failed晋升失败,full gc停顿
- 元空间耗尽导致fullgc
针对Concurrent mode failure
主要有两个方面原因导致
- 内存碎片
- 老年代空间较小,或者CMS收集不及时
这个时候,可以降低老年代的触发MinorGC的阈值,提前进行GC。或者适当增加老年代空间。
争对Promotion Failed
问题可以调整From To空间大小,开启FullGC压缩等手段。