JVM CMS垃圾回收器探索
CMS简介
CMS垃圾回收器是针对老年代的回收方式、是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这个方式非常适合。在JVM启动参数上面加上 XX:+UseConcMarkSweepGC 表示老年代使用CMS的方式回收,CMS的回收算法是 标记-清除
CMS执行过程
1.STW initial mark
1 | 2018-11-21T11:41:46.066+0800: 58424.191: [GC (CMS Initial Mark) [1 CMS-initial-mark: 3636058K(5592448K)] 3691980K(7829376K), 0.0037087 secs] [Times: user=0.06 sys=0.00, real=0.00secs] |
初始标记: 这个阶段JVM停止正在执行的任务。从根对象开始扫描和root对象直接关联的对象。这个过程会很快。
2.Concurrent marking
1 | 2018-11-21T11:41:46.070+0800: 58424.195: [CMS-concurrent-mark-start] |
并发标记: 这个阶段JVM标记线程和用户线程并发执行。在初始标记的基础上继续向下追溯扫描对象,这个过程不会停顿。
Concurrent precleaning
1 | 2018-11-21T11:41:46.122+0800: 58424.247: [CMS-concurrent-preclean-start] |
并发预清理: 这个阶段JVM标记线程和用户线程并发执行。JVM查找在并发标记阶段新进入老年代的对象。通过从新扫描减少下一个阶段的工作,这个过程不会停顿。
3.STW remark
1 | 2018-11-21T11:41:47.486+0800: 58425.611: [GC (CMS Final Remark) [YG occupancy: 1078027 K (2236928 K)] |
重新标记: 这个阶段JVM停止正在执行的任务。最终确认老年代中存活的对象,因为之前的处理都是并发的,应用程序也在不停的分配对象。
4.Concurrent sweeping
1 | 2018-11-21T11:41:47.616+0800: 58425.741: [CMS-concurrent-sweep-start] |
并发清理: 这个阶段JVM清理线程和用户线程并发执行,清理死亡对象。
5.Concurrent reset
1 | 2018-11-21T11:41:50.524+0800: 58428.649: [CMS-concurrent-reset-start] |
并发重置: 重置CMS收集器数据结构,等待下一次回收。
CMS参数优化
- 由于CMS垃圾回收器使用的是标记-清除回收算法,随着GC的执行老年代会逐渐产生内存碎片,可以使用 -XX:+UseCMSCompactAtFullCollection 这个参数在每次执行CMS GC之后进行一次压缩处理。可以根据自己的应用老年代对象的情况调整 -XX:CMSFullGCsBeforeCompaction=1 这个参数来控制每隔几次GC之后进行压缩操作。
- CMS在垃圾回收的过程当中,会不停的有新对象在老年代分配,在CMS回收之前必须留有一部分空间,所以CMS不会在老年代满了的时候开始回收,使用 -XX:CMSInitiatingOccupancyFraction=65 -XX:+UseCMSInitiatingOccupancyOnly 这个两个参数来配置老年代空间到达什么阈值之后开始回收。特别注意的是如果不配置第二个参数,CMS只会在第一次回收的按照设置的阈值,后面CMS会根据情况动态调整阈值参数。
- 由CMS回收的过程中我们可以知道,在初始标记和重新标记两个阶段会STW,所以为了使得这两个阶段尽可能快一点执行完成,可以使用 -XX:+CMSParallelInitialMarkEnabled -XX:+CMSParallelRemarkEnabled 这个参数来开启并行标记。
- 在CMS回收的过程中,耗时最长的是重新标记阶段,可以使用 -XX:+CMSScavengeBeforeRemark 这个参数在重新标记之前执行一次Young GC。这样Young区待标记的对象就会减少很多,重新标记阶段的工作量就会少很多。因为执行Young GC也有一定的耗时,所以这个参数需要自己做一个权衡。
人间有味是清欢