更多请关注 >>http://java.jr-jr.com

  1. 1. 总体介绍
  2. 2. CMS过程
    1. 2.1. 初始标记
    2. 2.2. 并发标记
    3. 2.3. 并发预清理
    4. 2.4. 重新标记
    5. 2.5. 并发清理
    6. 2.6. 并发重置
  3. 3. CMS缺点
  4. 4. 啥时候用CMS
  5. 5. CMS的全量GC
  6. 6. 永久代的回收

总体介绍

CMS是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动JVM参数加上-XX:+UseConcMarkSweepGC,这个参数表示对于老年代的回收采用CMS。CMS采用的基础算法是:标记—清除。在整个CMS的垃圾回收过程中,大部分是可以和用户的应用并发执行的,不需要暂停用户的应用。但是为了保证正确的运行,CMS仍旧需要少量的stop-the-world暂停。所以当人别人问你CMS是否有STW问题时,不要回答没有。

CMS过程

  • 初始标记(STW initial mark)
  • 并发标记(Concurrent marking)
  • 并发预清理(Concurrent precleaning)
  • 重新标记(STW remark)
  • 并发清理(Concurrent sweeping)
  • 并发重置(Concurrent reset)

初始标记

在这个阶段,需要虚拟机停顿正在执行的任务,官方的叫法STW(Stop The Word)。这个过程从垃圾回收的”根对象”开始,只扫描到能够和”根对象”直接关联的对象,并作标记。所以这个过程虽然暂停了整个JVM,但是很快就完成了

并发标记

这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记。并发标记阶段,应用程序的线程和并发标记的线程并发执行,所以用户不会感受到停顿。

并发预清理

并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行并发标记阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。通过重新扫描,减少下一个阶段”重新标记”的工作,因为下一个阶段会Stop The World。

重新标记

这个阶段会暂停虚拟机,收集器线程扫描在CMS堆中剩余的对象。扫描从”跟对象”开始向下追溯,并处理对象关联。

并发清理

清理垃圾对象,这个阶段收集器线程和应用程序线程并发执行。

并发重置

这个阶段,重置CMS收集器的数据结构,等待下一次垃圾回收。

CMS缺点

  • CMS回收器采用的基础算法是Mark-Sweep。所有CMS不会整理、压缩堆空间。这样就会有一个问题:经过CMS收集的堆会产生空间碎片。 CMS不对堆空间整理压缩节约了垃圾回收的停顿时间,但也带来的堆空间的浪费。为了解决堆空间浪费问题,CMS回收器不再采用简单的指针指向一块可用堆空间来为下次对象分配使用。而是把一些未分配的空间汇总成一个列表,当JVM分配对象空间的时候,会搜索这个列表找到足够大的空间来hold住这个对象。
  • 需要更多的CPU资源。为了让应用程序不停顿,CMS线程和应用程序线程并发执行,这样就需要有更多的CPU,单纯靠线程切 换是不靠谱的。并且,重新标记阶段,为空保证STW快速完成,也要用到更多的甚至所有的CPU资源。
  • CMS的另一个缺点是它需要更大的堆空间。因为CMS标记阶段应用程序的线程还是在执行的,那么就会有堆空间继续分配的情况,为了保证在CMS回 收完堆之前还有空间分配给正在运行的应用程序,必须预留一部分空间。也就是说,CMS不会在老年代满的时候才开始收集。相反,它会尝试更早的开始收集,已避免上面提到的情况:在回收完成之前,堆没有足够空间分配!默认当老年代使用68%的时候,CMS就开始行动了。 – XX:CMSInitiatingOccupancyFraction=n来设置这个阀值。总得来说,CMS回收器减少了回收的停顿时间,但是降低了堆空间的利用率。

啥时候用CMS

如果你的应用程序对停顿比较敏感,并且在应用程序运行的时候可以提供更大的内存和更多的CPU(也就是硬件牛逼),那么使用CMS来收集会给你带来好处。还有,如果在JVM中,有相对较多存活时间较长的对象(老年代比较大)会更适合使用CMS。

利用多核
通过使用以下的选项,可以使得CMS充分利用多核:
-XX:+CMSConcurrentMTEnabled 在并发阶段,可以利用多核
-XX:+ConcGCThreads 指定线程数量
-XX:+ParallelGCThreads 指定在stop-the-world过程中,垃圾回收的线程数,默认是cpu的个数
-XX:+UseParNewGC 年轻代采用并行的垃圾回收器

CMS的全量GC

如果CMS不能够在老年代清理出足够的空间,会导致异常,使得JVM临时启动Serial Old垃圾回收方式进行回收。这个会造成长时间的stop-the-world暂停。全量的GC的原因可能有两个:

  • CMS垃圾回收的速度跟不上了
  • 老年代中有大量的内存碎片
  • 没有为JVM分配足够多的内存,从而导致OutofMemoryException。

永久代的回收

默认情况下,CMS是不回收永久代中的垃圾的。如果在你的应用中使用了多个类加载器,或者反射机制,那么就需要对永久代进行回收。采用参数-XX:+CMSClassUnloadingEnabled会打开永久代的垃圾回收。