CMS基础
这个垃圾收集器官方名称是”Mostly Concurrent Mark and Sweep Garbage Collector“。它在年轻代使用”复制”算法,并行进行垃圾回收,在老年代使用并发的“标记清除”算法。
CMS收集器目标是避免在老年代长时间停顿,它通过两种方式实现。首先,它不压缩老年代空间,而是通过空闲列表来管理回收空间。其次,它与应用程序线程同时完成标记和清理阶段的大部分工作。这意味着垃圾收集器不会显式停止应用程序线程来执行这些阶段,然后这也会导致垃圾回收线程和应用程序线程会竞争CPU时间。
CMS收集阶段
第一阶段:初始标记
该阶段会触发stop-the-world暂停。这个阶段的目标是标记老年代中的所有对象,这些对象要么是直接的GC根,要么是从年轻代中某个活动对象引用的。后者很重要,因为老年代是单独收集的。
第二阶段:并发标记
在这个阶段,垃圾收集器会遍历老年代并标记所有活着的对象,从“初始标记”阶段找到的GC根开始。“并发标记”阶段,顾名思义,就是与应用程序线程并发运行,不会停止应用程序线程。请注意,此阶段并不是所有的老年代存活对象都可以被标记,因为应用程序在标记期间会改变引用。
第三阶段:并发预清理
这又是一个并发阶段,与应用程序线程并行运行,而不是停止它们。由于前一阶段与应用程序线程同时运行,一些引用已被更改。每当发生这种情况时,JVM会将这些突变对象的区域标记为Dirty Card。
在预清理阶段,那些能够从Dirty Card区域可达的对象也被标记为存活,当标记完这些对象后,该Dirty Card区域就会消失。
第四阶段:并发可中断预清理
与并发预处理清理类似,CMS有两个参数:CMSScheduleRemarkEdenSizeThreshold、CMSScheduleRemarkEdenPenetration,默认值分别是2M、50%。这两个参数组合起来的意思是预清理后,Eden空间使用超过2M时启动可中断的并发预处理,直到Eden空间使用率达到50%时中断,进入重新标记阶段。
第五阶段:最终标记
这是CMS垃圾收集的第二个也是最后一个stop-the-world暂停。这个阶段目标是完成老年代所有活动对象的标记。由于之前的预清理阶段是并发的,它无法跟上应用程序线程的变化速度,需要stop-the-world暂停才能完成。
通常CMS会在Young Generation尽可能发生后尝试运行“最终标记”阶段,以消除多个stop-the-world暂停。
第六阶段:并发清理
无需stop-the-world暂停,与应用程序线程同时运行。该阶段目标是清理未使用的对象并回收它们的空间。
第七阶段:并发重置
清理并恢复在CMS GC过程中的各种状态,重新初始化CMS相关数据结构,为下一个垃圾收集做准备。
总而言之,CMS垃圾收集器通过将大量工作分成不同阶段,在大部分阶段不需要stop-the-world暂停,从而减少延迟。然而,CMS也存在很多缺点,其中最明显的就是由于CMS使用“标记清除”算法清理老年代,所以会产生内存碎片;其次CMS并发清理阶段是和应用程序线程同时进行的,会产生浮动垃圾,CMS无法在当次的收集中处理掉它,只好等到下一次GC去处理;还有CMS并发清理失败后,会使用Serial Old收集器进行Full GC,由于Serial收集器是单线程、存在stop-the-world暂停,会出现长时间的暂停。
CMS优化
-XX:+UseConcMarkSweepGC
启用CMS垃圾收集器。默认情况下,Java任何版本都没有将CMS做为默认的垃圾收集器。
-XX:+UseParNewGC
一般与CMS配合使用,ParNew是年轻代的多线程垃圾收集器,使用“复制”算法收集垃圾。
-XX:+CMSConcurrentMTEnabled
激活此选项后,允许CMS在并发阶段使用多个线程运行。默认情况下,该选项已激活。
-XX:ConcGCThreads
设置CMS并发阶段的运行线程数。该值取决于ParallelGCThreads,ParallelGCThreads一般默认情况是系统内核数,ConcGCThreads=(ParallelGCThreads+3)/4。
-XX:CMSInitiatingOccupancyFraction
CMS仅在堆已满时才启动GC,即当没有足够的可用空间来存储新分配的对象或提升对象。对于CMS来说,不建议等待这么长时间,CMS收集器需要更早地启动GC。通过设置CMSInitiatingOccupancyFraction阈值提示CMS进行GC。默认值为68%。
-XX:+CMSInitiatingOccupancyOnly
后台线程ConcurrentMarkSweepThread循环判断(默认2s)是否触发GC,这种现象称为周期性Old GC。如果没有设置CMSInitiatingOccupancyOnly,虚拟机会根据收集的数据决定是否触发GC(建议生产环境带上这个参数)。启用改选项,JVM会为每一个CMS GC使用CMSInitiatingOccupancyFraction做为GC启动的阈值。
-XX:+CMSClassUnloadingEnabled
CMS是对老年代进行垃圾收集活动的,默认情况下不会对永久代执行GC。激活该选项,CMS会对永久代进行垃圾收集。
-XX:CMSInitiatingPermOccupancyFraction
设置该选项表示当永久代占比达到阈值,就触发CMS GC回收永久代对象,前提是开启CMSClassUnloadingEnabled选项。
-XX:+CMSScavengeBeforeRemark
激活该选项表示在CMS GC前启动一次Young GC,主要减少Old区对Young区的引用,降低CMS最终标记阶段的开销,减少STW时间。
-XX:+CMSIncrementalMode
激活该选项表示启用CMS收集器的增量模式。增量模式会定期暂停CMS并发阶段,完全让步给应用程序线程,所以CMS收集器需要更长的时间来完成整个GC。因此只有当CMS GC对应用程序线程影响过多的情况下,启用该选项这更有意义。
-XX:+ExplicitGCInvokesConcurrent
激活该选项表示避免当应用程序调用System.gc()来显式调用GC。当应用程序调用System.gc()时,会触发Full GC,激活该选项指示JVM运行CMS GC而不是完整的Full GC,从而减少STW暂停时间。
-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
激活该选项表示在调用系统GC时,确保CMS GC将永久代包含进去。
-XX:+DisableExplicitGC
激活该选项表示忽略系统GC。