CMS 垃圾收集器
一、如何使用
1999 年 jdk1.3.1 发布了 serial GC 收集器
2002 年 jdk1.4 发布,Parallel GC 和 CMS 一起发布
Parallel GC 在 JDK6 之后成为 Hotspot 默认垃圾收集器
serial GC、ParallelGC、CMS 之间比较:
如果你想最小化的使用内存和并行开销,请选用 serial GC
如果你想最大化应用的吞吐量,请使用 Parallel
如果你想最小化 GC 的中断或停顿时间,请选用 CMS
开启方式:
使用 serial:-XX:+UseSerialGC
使用 ParNew:-XX:+UseParNewGC (老年代使用 serial Old)
使用 Parallel:-XX:+UseParallelGC (PS + serial Old)、-XX:+UseParallelOldGC (PS + PS Old)
使用 CMS:-XX:UseConcMarkSweepGC (年轻代使用 ParNew)
二、回收流程
1、初始标记
会发生 STW,仅仅标记 GC ROOT,过程非常快。容易理解
2、并发标记
此阶段是根据 GC ROOT 全堆 Trace 的过程,与用户线程并发执行。
此阶段可能会产生三个问题:
首先需要明白一个东西:CMS 回收是针对老年代的:
老年的的对象有两个来源:
1)由新生代晋升而来
2)大对象直接分配在老年代。由 -XX:PretenureSizeThreshold 控制
a、如果用户程序产生了新的引用(比如在栈上产生了一个 GC ROOT),怎么办?
此时这个对象会被直接分配到 CMS 预留的空间中。这个空间不会发生 GC。
b、如果已经标记完成的对象、指向了一个新的引用怎么办?
通过写屏障记录新产生的引用,重新标记阶段会以其为 GC ROOT 重新扫描
c、如果已经标记完的对象,删除了一个引用怎么办?
此时 CMS 会不处理这种情况,无用引用,因为已经标记了,所以不会被本次回收。此时是 CMS 的问题:会产生浮动垃圾。
3、重新标记
STW、修正并发标记期间因用户程序产生的对象引用的变更。
4、并发清除
并发清除产生的由 并发标记、重新标记阶段 标记的垃圾。注意:此阶段用户线程也在跑,所以用户线程产生的引用不应该被清理。
三、何时触发 GC
1、Promotion Fail:年轻代晋升失败
2、Comcurrent Mode failure:CMS GC 期间,预留空间不够用户线程使用。通过 -XX:+CMSInitiatingOccupancyFaraction 设置阈值。老年代使用率超过多少会触发 GC。此时是只是老年代 GC,在分代模型中,仅仅这个会发生 Major GC。其余都是 Full GC。
四:缺点
因为 CMS 是多线程的,如果并行开销大的话,会影响用户线程执行
浮动垃圾
内存碎片
四、调优参数
-XX:ParallelGCThreads 设置年轻代 ParNew 使用的线程数。默认开启的线程数量与 CPU 核数相同
-XX:ConcGCThreads 早期也叫 ParallelCMSThreads。默认为 (ParallelGCThreads + 3) / 4.也就是说 4 核时,CMS 线程数为 1。8 核时,线程数为 2.
-XX:+UseCMSCompactAtFullCollection 默认为 0,执行几次 Full GC 以后会进行空间整理。
-XX:+CMSInitiatingOccupancyFaraction 默认为 92%,指定老年代使用超过多少一个进行仅限于老年代的回收。
五、增量更新
所谓增量更新就是在并发标记过程中,把赋值的这种新增的引用,做一个集合存起来。在重新标记的时候会找到集合里面的引用然后重新去扫描,再把源头标记为灰色。这就是我们的增量更新。当然,在把我们新增的引用放到集合的时候,会实现一种写屏障的方式。写屏障我们可以理解为在赋值操作的前面加一个方法,赋值的后面做一些操作,也可以理解为 AOP。
评论