回顾分代GC和CMS、
并发标记扫描收集器(CMS),也被称作低暂停并发收集器。它是回收老年代。它试图利用和应用线程并发的收集尽可能多的垃圾,以减少因为垃圾收集导致的停顿。通常这种低延迟并发收集器不会对活动的对象进行压缩处理,也就是说它只做一个不对对象进行移动的垃圾回收,那么配分一个更大的内存的时候,碎片就会成为一个问题。
注意:CMS收集器在年轻代中使用和parallel 收集器相同的算法。
CMS收集阶段
CMS收集器在堆的老年代的回收上有以下几个阶段:
阶段 | 描述 |
---|---|
初始化标记 (stop-the-world事件) |
标记老年代中依然存活的对象,包括那些从年轻代晋升来的,这个过程通常比较短暂(译者注:存活我理解为被程序引用,不可回收的对象) |
并发标记 | 和应用程序并发执行的,扫描整个老年代,是从那些标记的对象,或者间接标记的对象开始扫描。第2、3、5个阶段是并发执行的,在这个过程中,在CMS的代中新分配的对象(包括晋升的对象)会被立即标记为存活状态。 |
重新标记 (stop-the-world事件) |
该阶段发现那些被并发标记错过的对象,因为并发标记是和应用程序并发执行的,在标记线程完成对某个对象的跟踪那刻,应用程序可能对对象进行了更新。 |
并发清理 | 收集那些在标记阶段没有标记的对象,消亡对象所占的空间会被添加到释放列表里用于重新分配,对死亡对象的聚集工作就发生在这个点。注意:存活的对象不会被移动。 |
并发重置 | 做一些数据结构的清理工作,为下一次收集做准备 |
回顾垃圾收集的步骤
接下来,让我们回顾一下CMS GC的操作步骤:
1、CMS GC的堆结构
整个堆被分成三个部分
年轻代被分割成一个Eden和和两个survivor区域,老年代是延续空间,对象的收集就在这片区域,没有压缩操作直到一个full GC发生。(译者注:读到这里,我们应该很清楚压缩的意思就是说不仅仅对垃圾对象进行收集,还对存活的对象进行腾挪,聚集在一起,腾出更多的空间,不要让对象散落在不同的空间,这就是内存碎片,内存碎片导致无法分配一块连续的大内存)
2、在CMS中,年轻代的GC是怎么工作的?
年轻代被标记为绿色,老年代为蓝色,你的JAVA程序在运行一段时间后,可能会像下面这个样子.对象分散的分布在老年代中:
使用CMS,老年代是对象消亡的地方,它们不会被移动,也不会被压缩,除非遇到一个Full GC.
3、年轻代的收集
存活的对象从 Eden 区域或者一个 survivor区域,移动到另外一个survivor区域,一些老的对象如果达到了晋升阈值,就会被提升到老年代中。
4、年轻代GC之后
年轻代的收集之后,Eden区域和其中一个survivor区域会被清理
新晋升的对象是深蓝色区域,绿色区域年轻代中存活下来,还没有晋升到老年代的对象。
5、CMS中老年代的收集
两次stop_the_world的时间是初始化标记和重新标记阶段,当老年代达到一定的占有率,CMS才开始登场了。
(1)初始化标记是对存活的对象进行标记,只有短暂的停顿(2) 并发标记阶段,是和应用程序一起执行的,也害死标记存活的对象。最终在第(3)阶段去标记那些上一阶段(2)遗漏的对象。
6、老年代的收集-并发清理
在上一阶段没有被标记的对象会在这里消亡,这里没有压缩。
注意:未标记的对象就等于要消亡的对象(死亡对象)。
7、老年代的收集-清理过后
经过第(4)清理阶段之后,你会发现很多内存被释放了,你也会发现这里仍然没有压缩
最终,CMS收集器会经过第(5)重置阶段,等待下一次满足GC条件(译者注:就是上文说到的,老年代达到了一定的占有率)。
除非注明,赵岩的博客文章均为原创,转载请以链接形式标明本文地址
本文地址:https://zhaoyanblog.com/archives/402.html