CMS垃圾回收器
cms是以获取最短垃圾收集停顿时间为目标的收集器,关注点在于尽可能缩短垃圾收集时用户线程的暂停时间,停顿时间越短就越适合与用户交互的程序,CMS收集器使用的算法是标记-清除算法实现的;
CMS步骤
这里以周志明老师的4个步骤来介绍
-
初始标记(Initial-Mark)(需要暂停)
这个阶段程序所有的工作线程都将会因为"Stop-the-Wold"机制而出现短暂,主要标记从GC Roots能够直接关联的对象。图解:
此处仅标记CG Roots能直接关联到的ABC对象 -
并发标记(Concurrent-Mark)(无需暂停)
使用三色标记法从GC Roots能直接关联到的对象开始遍历整个对象图的过程,如果此时用户线程没有对上述图片的对象关系发生改动,则结果为:
但是,由于此过程没有对用户线程进行暂停,所以就会出现两种情况:
-
情况一:原来可达的对象变成不可达
当发生情况一时,已经被标记为黑色的对象I,从原先可达变成不可达,那么就会产生浮动垃圾。无法在此次垃圾收集中被回收。
如图所示:
-
情况二:原来不可达的对象变成可达
当发生情况二时,用户线程从对象D(黑色)建立了到对象F(白色)之间的引用,那么就会对产生从黑色对象到白色对象之间的引用关系,此时会将新插入的这个引用关系给记录下来,等并发扫描结束后,以这些记录过的引用关系中黑色对象为根重新扫描。这个过程被称为:增量更新
记录后的结果:
此时,被记录的黑色对象变成灰色,代表其子节点未完全扫描完
重新扫描后:
- 重新标记(Remark)(需要暂停)
修正并发标记期间用户程序运行导致标记变动的那一部分对象的标记记录。
在并发标记阶段,如果用户生成了新对象,并且跟GC Roots产生联系,那么会在此阶段重新标记上。由于大部分与GC Roots直接关联的对象已经在第一阶段处理完成,此阶段只需要处理新的与GC Roots建立关系的对象即可,所以会时间并没有那么长。下面看看图片理解下。
重新标记开始:
标记结束:
注:在此阶段,会遍历卡表中为脏的元素,得出哪些卡页存在跨代指针,一同加入到GC Roots中进行扫描,这里针对cms的回收过程,就不对记忆集和卡表敞开说明了。 - 并发清除(Concurrent-Sweep)(无需暂停)
此阶段清理删除掉标记判断已经死亡的对象,并释放内存空间。由于使用的是标记-清除算法,无需移动存活对象位置,所以无需对用户线程暂停。
CMS缺点
- 由于需要与用户线程并行执行,所以会占用用户线程的CPU资源。CMS默认启动的回收线程数是(处理器核心数量 +3) /4,可以看出,当CPU数小于4时,会占用大量用户线程的CPU资源去执行收集器的线程。
- 无法处理浮动垃圾
由于收集过程与用户线程并行执行,所以需要预留一部分空间给用户线程在收集时使用,并且在用户线程执行时候出现新的垃圾时需要等待下一次垃圾回收才能被回收掉。 - 空间碎片问题
CMS是一款基于标记-清除算法实现的收集器,所有会有空间碎片的现象。当空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。