垃圾收集器的判断指标
衡量垃圾收集器的三项重要指标是:内存占用,吞吐量,和延迟,三者构成了一个不可能三角,好的垃圾收集器应该尽可能满足其中的两项。本文分析的CMS垃圾收集器着重于低延迟,如果你希望你的服务器停顿延迟短,CMS就非常满足这类应用的需求。
CMS收集器介绍
CMS(Concurrent Mark Sweep)收集器是用于收集老年代的收集器,采用标记清除算法,下面这张图很好的解释了CMS的执行过程:
一共可以划为4步:1)初始标记,这一步很快,只是标记一下GCROOTS中能直接关联到的对象 2)并发标记,这一步就是遍历对象图的过程,比较耗时,但CMS在这一步实现了并发 3)重新标记,CMS使用的是增量更新,较原始快照会慢一点,但是浮动垃圾会少一点 4)并发清除,可以注意到采用标记-清除不需要移动对象,我们可以做到并发,那他会有那些缺点呢?(先抛出一个问题
CMS垃圾收集器缺点
Hotspot虚拟机的线程是1对1线程模型。在并发阶段,垃圾收集线程虽然不会导致整个应用停下来,但是会占用处理器资源,还是会导致应用变慢。具体的默认回收线程数是(处理器核心数 +3)/4/。
其次,CMS垃圾收集器无法处理浮动垃圾(在并发阶段,随着用户线程持续运行还是会有垃圾不断产生,CMS无法处理这些垃圾),同时在并发阶段,用户线程也需要一定的空闲堆空间来保证运行,那么如果此时空间不够了,就会导致并发失败(Concurrent Mode Failure),此时采用Serial Old来重新收集老年代。再思考一下,为什么Serial Old而不用Parallel Old收集器呢?他们都要Stop the world呀。因为此时堆内存已经很紧张了,我们还要开多个线程,不是更完蛋吗。
最后,由于采用标记-清除算法,我们在清除时是方便了,但这会导致很多的内存碎片。当空闲内存无法满足对象的分配要求时,我们又会提前进行一次Full GC。好消息是通过参数我们可以改善这个问题:-XX:CMSFullGCsBefore-Compaction(此参数从Jdk 9 开始废弃),这个玩意配置后,我们经过配置的参数次不整理的Full GC后,在下一次进行收集前会进行碎片整理。