据我所知,CMS收集器收集老一代,它与ParNew收集器(用于收集年轻一代)一起工作。 我很难清楚地理解CMS是如何工作的,但这就是我看到它的方式:
1)初始标记。 寻找根参考。由于收集器是oldgen收集器,它应该只扫描老一代。
2)Concurrent-Mark 找到所有根引用后,就可以开始并发标记了。从第一阶段标记的对象可以传递的所有对象都标记在此阶段。
3)并发Preclean gc查看CMS堆中的对象,这些对象通过年轻代或新分配的促销更新,或者在我们在前一个并发标记阶段进行并发标记时由mutator更新。 [请确认那1这个阶段的唯一目的是做一些必须在下一阶段完成的工作(备注)? 2)有一些过程正在查看在并发标记阶段期间哪些参考被更改。 请告诉我这两项是否正确]
4)备注 gc停止世界,然后查看CMS堆中的对象,这些对象通过年轻代或新分配的促销更新,或者在我们进行并发预清洁时由mutator更新。
但今天我看到了这篇文章
初始标记在初始标记期间,CMS应收集所有根 参考开始标记旧空间。这包括:参考 来自线程堆栈,来自年轻空间的引用。参考文献来自 堆栈通常很快(小于1毫秒)收集,但时间到了 从年轻空间收集参考资料取决于对象的大小 年轻的空间。通常初始标记在年轻空间之后开始 集合,所以伊甸园空间是空的,只有活的物体在其中一个 幸存者空间。幸存者空间通常很小并且之后是初始标记 年轻的空间收集通常需要不到毫秒。但如果 当伊甸园已满时,初始标记开始可能需要很长时间 (通常比年轻的太空收藏本身更长)。一旦CMS 集合被触发,JVM可能会等待一段时间进行年轻收集 在它开始初始标记之前发生。 JVM配置 选项-XX:CMSWaitDuration =可用于设置CMS等待的时间 在初始标记开始之前用于年轻空间收集。如果你 想避免长时间的初始标记暂停,你应该配置它 时间长于你的年轻收藏品的典型时期 应用
备注大多数标记与应用程序并行完成,但它 可能不准确,因为应用程序可能会修改对象图 标记。并发标记完成后;垃圾收集器应该 停止应用程序并重复标记以确保所有可访问 标记为活着的对象。但收藏家不必遍历 通过整个对象图;它应该只遍历参考修改 自标记开始(实际上自开始预清洁阶段)。卡 table(参见卡片标记写屏障)用于标识修改 旧空间中的部分内存,但是线程堆栈和年轻空间 应再次扫描。 通常评论阶段的大部分时间是 花在扫描年轻空间。如果我们这个时间会短得多 在开始评论之前在年轻的空间收集垃圾。我们可以 指示JVM在CMS备注之前始终强制进行年轻空间收集。 使用JVM参数-XX:+ CMSScavengeBeforeRemark启用此选项。 即使年轻的空间是空的,评论阶段仍然需要扫描 在旧空间中修改了引用,这通常需要时间接近 正常的年轻收集暂停(由于扫描过去的旧空间) 年轻的收集类似于备注所需的扫描。
http://blog.griddynamics.com/2011/06/understanding-gc-pauses-in-jvm-hotspots_02.html
不明白为什么CMS需要扫描年轻一代。为什么旧代垃圾收集需要它?
答案 0 :(得分:2)
你可能有包含循环引用的类,比如A类引用了B类,而B有A的反向引用。如果你有对象a和b属于这些类,并互相引用,gc从“外部”删除对它们的最后一个引用时必须删除它们。当然,包含更多元素的参考循环会使情况复杂得多。因此,gc必须检查哪些元素可以从某个根访问,哪些元素被引用但不可访问,应该被收集。
现在,如果你有代码中的某个地方
Object a=new A(new B(new C(new D())))
构造函数可能需要一些时间才能分配。但是你不希望gc删除新创建的D,只是因为C的构造函数需要一段时间才能运行,a
尚未分配。因此,您还需要扫描年轻一代,以捕获太小而无法从堆中引用的对象。
答案 1 :(得分:2)
Java堆分为两部分,它们是独立收集的:旧空间和年轻空间。
要收集任何空间,您需要在空间外找到所有入站参考。他们是:
旧的收藏应该扫描年轻的空间没有区别,年轻的收藏应该扫描旧的空间。
卡表写屏障用于不扫描每个年轻集合的整个旧空间(只有一小部分旧空间包含指向年轻人的链接,写屏障有助于跟踪这些区域)。
但是年轻空间没有卡表,所以旧的收藏应扫描整个内存范围。
PS我是您引用的文章的作者,您可以在my blog找到更多与GC相关的文章