JVM垃圾回收之CMS收集器

从前文JVM垃圾回收几种常见算法和常见收集器我们知道,CMS是老年代垃圾收集器。CMS 收集器主要关注系统停顿时间。CMS 是 Concurrent Mark Sweep 的缩写,意为并发标记清除,从名称上可以得知,它使用的是标记-清除算法,同时它又是一个使用多线程并发回收的垃圾收集器。它可以与Serial收集器和parNew收集器搭配使用。

JVM垃圾回收之CMS收集器

    CMS工作时,主要步骤有:初始标记、并发标记、重新标记、并发清除和并发重置。其中初始标记和重新标记是独占系统资源的,而并发标记、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上来说,CMS收集不是独占式的,它可以在应用程序运行过程中进行垃圾回收。

根据标记-清除算法,初始标记、并发标记和重新标记都是为了标记出需要回收的对象。并发清理则是在标记完成后,正式回收垃圾对象;并发重置是指在垃圾回收完成后,重新初始化CMS数据结构和数据,为下一次垃圾回收做好准备。

CMS收集器在其主要的工作阶段虽然没有暴力地彻底暂停应用程序线程,但是由于它和应用程序线程并发执行,相互抢占CPU,所以在CMS执行期内对应用程序吞吐量造成一定影响。CMS 默认启动的线程数是 (ParallelGCThreads+3)/4),ParallelGCThreads 是新生代并行收集器的线程数,也可以通过-XX:ParallelCMSThreads参数手工设定CMS的线程数量。当CPU资源比较紧张时,受到CMS收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕。

由于CMS收集器不是独占式的回收器,在CMS回收过程中,应用程序仍然在不停地工作。在应用程序工作过程中,又会不断地产生垃圾。这些新生成的垃圾在当前CMS回收过程中是无法清除的。同时,因为应用程序没有中断,所以在CMS回收过程中,还应该确保应用程序有足够的内存可用。因此,CMS收集器不会等待堆内存饱和时才进行垃圾回收,而是当前堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。这个回收阈值可以使用-XX:CMSInitiatingOccupancyFraction来指定,默认是 68。即当老年代的空间使用率达到 68%时,会执行一次CMS回收。如果应用程序的内存使用率增长很快,在CMS的执行过程中,已经出现了内存不足的情况,此时CMS回收将会失败,JVM将启动老年代串行收集器进行垃圾回收。如果这样,应用程序将完全中断,直到垃圾收集完成,这时,应用程序的停顿时间可能很长。因此,根据应用程序的特点,可以对-XX:CMSInitiatingOccupancyFraction 进行调优。如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低CMS的触发频率,减少老年代回收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。    标记-清除算法将会造成大量内存碎片,离散的可用空间无法分配较大的对象。在这种情况下,即使堆内存仍然有较大的剩余空间,也可能会*进行一次垃圾回收,以换取一块可用的连续内存,这种现象对系统性能是相当不利的。为了解决这个问题,CMS收集器还提供了几个用于内存压缩整理的算法。

  • -XX:+UseCMSCompactAtFullCollection 使CMS在垃圾收集完成后,进行一次内存碎片整理。内存碎片的整理并不是并发进行的。

  • -XX:CMSFullGCsBeforeCompaction 用于设定进行多少次CMS回收后,进行一次内存整理。

  • -XX:+CMSScavengeBeforeRemark 最终标记之前强制进行一个Minor GC。

  • -XX:+UseConcMarkSweepGC 可以要求新生代使用parNew,老年代使用CMS。

  • -XX:+UseCMSInitiatingOccupancyOnly 

    指定CMSInitiatingOccupancyFraction的回收阈值,如果不指定,jvm仅在第一次使用设定值,后续则自动调整。

  • -XX:+CMSParallelRemarkEnabled 并行运行最终标记阶段,加快最终标记的速度

  • -XX:+CMSClassUnloadingEnabled 让CMS可以收集永久带,默认不会收集

  • -XX:+ExplicitGCInvokesConcurrent 当调用System.gc()的时候,执行并行gc,只有在CMS或者G1下该参数才有效

CMS对CPU的资源非常敏感。由于采用标记-清除算法会存在空间碎片的问题,导致大对象无法分配空间不得不提前触发一次full gc的情况。而且无法处理浮动垃圾,可能出现Concurrent Model Failure失败而导致另一次full gc的情况。

垃圾回收篇幅过长,下文继续讲解最后的内容G1收集器。

上一篇:Junit单元测试


下一篇:深入浅出-iOS程序性能优化 (转载)