JVM学习笔记15

57 阅读2分钟

GC算法之CMS(Concurrent Mark-Sweep)

针对年轻代,它使用了parallel stop-the-world mark-copy algorithm(类似之前的serial GC和Parallel GC)。

针对老年代,它使用了mostly concurrent mark-sweep algorithm(2次stop the world)。

老年代的回收分为以下几个阶段:

initial mark (stop the world)

用于标识可从老年代外部访问的对象集.

concurrent marking

它标记了所有可以从上一个阶段的集合传递可达的活动对象。

因为应用程序正在运行并且它可能在标记阶段发生时更新引用字段(因此,修改对象图),所以不能保证在并发标记阶段结束时标记所有活动对象。

concurrent pre-cleaning

它发生在concurrent marking阶段之后和remark阶段之前,并完成一些在remark阶段应该完成的工作,即重新访问在concurrent marking阶段修改的对象。即使仍然需要remark阶段来完成标记(假设应用程序可能在concurrent pre-cleaning阶段更新更多对象),concurrent pre-cleaning的使用可以减少,有时会显著减少需要在remark阶段重新标记的对象的数量,因此,它减少了remark阶段的持续时间。

remark (stop the world)

这个 stop-the-world 阶段的目标是最终标记老年代中的所有活动对象。由于之前的预清理阶段是并发的,可能在concurrent pre-cleaning阶段发生时更新引用字段。card table数据结构被重用以跟踪修改的对象。因为 remark pause 比 initial mark 更重要,所以它是并行的以提高效率。

concurrent sweeping

它清除 Java 堆,释放垃圾对象而不重新定位活动对象。

这个阶段产生的空闲空间不是连续的(与前面两个垃圾收集器不同),垃圾收集器需要使用一个数据结构(free lists)来记录哪部分堆包含可用空间。

因此,分配内存到老年代的成本更高,因为从空闲列表(free lists)分配的效率不如 bump-the-pointer 方法。这给次要垃圾回收带来了额外的开销,因为老年代中的大多数分配都是在次要垃圾回收期间提升对象时发生的。

在标记阶段变成垃圾的对象可能会或可能不会在当前gc周期中被回收。如果不会,则将在下一个周期回收它们。在垃圾回收期间未识别的垃圾对象通常称为浮动垃圾。

最后,由于缺乏压缩而导致的碎片问题也可能会阻止垃圾收集器尽可能有效地使用所有可用的空闲空间。如果在回收到足够的空间之前老年代已满,CMS 将恢复到昂贵的停止世界压缩阶段,类似于并行和串行 GC。