上一篇文章已经了解了传统的垃圾回收器和CMS回收器。但是关于CMS回收器的一些知识并未全部概述。
我们都知道,在并发清除的时候,用户线程和GC线程是可以同时运行的,但是在这清除的过程中可能还会产生垃圾对象。这些垃圾对象,无法在本次垃圾清理的时候清理掉,这些垃圾也有一个名字,叫做:浮动垃圾。
浮动垃圾
CMS将无法对这些垃圾对象进行标记,由于GC线程与用户线程同时运行,最终会导致这些新产生的垃圾对象,没有被及时的回收,从而只能在下一次执行GC时释放这些之前未被回收的对象。这些垃圾也就是浮动垃圾。
触发stw机制的次数
在前面我们还是稍微提到过,说的是两次,但是并未考虑到在并发清理之后发生的碎片化问题,碎片化的问题导致我们可能很难存储大对象,那么一旦大对象需要存入,而CMS回收器回收了并没有足够空间,就会触发到FullGC进行垃圾回收。
CMS核心配置参数
-XX:+UseConcMarkSweepGc
手动指定使用CMS收集器执行内存回收任务。开启该参数后会自动将-XX: +UseParNewGc打开。即: ParNew (Young区用) +CMS (0ld区用) +Serial 0ld的组合。这里提到的就是重点,这里可以看到老年代才是使用的CMS,年轻代默认使用的是ParNew。-XX:CMS1nitiatingOccupanyFraction
设置堆内存使用率的阈值,一旦达到该阈值,便开始进行回收。JDK5及以前版本的默认值为68,即当老年代的空间使用率达到68%时,会执行一 次CMS 回收。 JDK6 及以上版本默认值为92%。 如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低CMS的触发频率,减少老年代回收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。因此通过该选项便可以有效降低Full GC的执行次数。-XX: +UseCMSCompactAtFullCollection
用于指定在执行完Full GC后对内存空间进行压缩整理,以此避免内存碎片的产生。不过由于内存压缩整理过程无法并发执行,所带来的问题就是停顿时间变得更长了。-XX:CMSFullGCsBeforeCompaction
设置在执行多少次Full GC后对内存空间进行压缩整理。建议1次-XX:ParallelCMSThreads
设置CMS的线程数量。CMS 默认启动的线程数是(ParallelGCThreads+3)/4, ParallelGCThreads是年轻代并行收集器的线程数。当CPU资源比较紧张时,受到CMs收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕。新生代回收线程数最好和当前cpu核数相等。
这里我们在来总结一下,CMS的优缺点。
总结
- 优点
1.减少STW时间
2.GC线程与用户线程同时执行 - 缺点
1.产生碎片化问题
处理方案:直接触发FullGC,采用老年代串行回收器清理整个堆内存垃圾,采用标记整理算法,导致所有用户线程阻塞
2.多核线程消耗cpu资源
3.浮动垃圾
4.存放大对象可能触发fullGC