当前位置: 首页 > 知识库问答 >
问题:

为什么CMS收集器在初始标记阶段收集年轻一代的根引用?

谷梁德容
2023-03-14

据我所知,CMS collector收集老一代,它与ParNew collector(用于收集年轻一代)一起工作。对我来说,要清楚地理解CMS是如何工作的并不容易,但以下是我的看法:

1)初始标记。寻找根引用。由于收集器是旧的收集器,它应该只扫描旧的一代。

2) 并发标记找到所有根引用后,是时候开始并发标记了。从第一阶段中标记的对象可传递到达的所有对象都在该阶段中标记。

3) 并发预清理gc查看CMS堆中的对象,这些对象是通过年轻一代的升级或新分配更新的,或者是在我们在上一个并发标记阶段进行并发标记时通过变异程序更新的。【请确认1本阶段的唯一目的是完成下一阶段必须完成的部分工作(备注)?2)有一些程序正在查看在并发标记阶段更改了哪些参考。请告诉我这两项是否正确】

4) 备注:gc停止运行,然后查看CMS堆中的对象,这些对象是通过年轻一代的升级或新分配更新的,或者是在我们进行并行预清理时通过变体更新的。

但今天我看到了这篇文章

初始标记在初始标记期间,CMS应收集所有根引用,以开始标记旧空间。这包括:来自线程堆栈的引用,来自young space的引用。从堆栈中收集引用通常非常快(小于1ms),但从年轻空间收集引用的时间取决于年轻空间中对象的大小。通常初始标记在年轻的空间收集之后立即开始,所以伊甸园空间是空的,只有活的对象在幸存者空间中。幸存者空间通常很小,年轻空间收集后的初始标记通常不到毫秒。但如果最初的标记是在伊甸园满的时候开始的,可能需要相当长的时间(通常比年轻的空间收藏本身要长)。一旦触发CMS收集,JVM可能会等待一段时间,等待年轻的收集发生,然后才开始初始标记。JVM配置选项–XX:CMSWaitDuration=可用于设置CMS在开始初始标记之前等待年轻空间收集的时间。如果希望避免长时间的初始标记暂停,则应将此时间配置为比应用程序中典型的年轻集合时间长。

备注:大多数标记与应用程序并行进行,但可能不准确,因为应用程序可能会在标记期间修改对象图。同时标记完成时;垃圾收集器应该停止应用程序并重复标记,以确保所有可访问的对象都标记为活动对象。但收集器不必遍历整个对象图;它应该只遍历自标记开始(实际上是自预清洁阶段开始)以来修改的参考。Card table(参见Card marking write barrier)用于识别旧空间中内存的修改部分,但应再次扫描线程堆栈和新空间。通常,评论阶段的大部分时间都用来扫描年轻的空间。如果我们在开始评论之前在年轻的空间收集垃圾,这个时间会短得多。我们可以指示JVM总是在CMS评论之前强制年轻的空间收集。使用JVM参数–XX:CMSScavengeBeforeRemark启用此选项。即使年轻的空间是空的,备注阶段仍然必须扫描旧空间中修改过的引用,这通常需要接近正常年轻收集暂停的时间(年轻收集期间对旧空间的扫描与备注所需的扫描类似)。

http://blog.griddynamics.com/2011/06/understanding-gc-pauses-in-jvm-hotspots_02.html

我不明白为什么CMS需要扫描年轻一代。为什么旧一代的垃圾收集需要它?

共有2个答案

洪知
2023-03-14

Java堆被分成独立收集的两部分:旧空间和年轻空间。

要收集任何一个空间,您需要找到空间之外的所有入站引用。他们是:

  • 堆栈中的局部变量
  • 嵌入到JIT编译代码块中的文字
  • 来自另一个空间的所有引用

旧的收藏应该扫描年轻的空间,而年轻的收藏应该扫描旧的空间,两者没有区别。

卡片表写屏障用于不扫描每个年轻集合的整个旧空间(只有一小部分旧空间包含指向年轻的链接,写屏障有助于跟踪该区域)。

但年轻的空间没有卡片桌,所以旧的收藏应该扫描整个存储范围。

PS我是你参考过的文章的作者,你可以在我的博客上找到更多GC相关的文章

慕容聪
2023-03-14

你可能有循环引用的类,比如a类有对B类的引用,B类有对a的反向引用。如果你有属于这些类的对象a和B,并且相互引用,gc必须在你从“外部”删除对它们的最后一个引用时删除它们。当然,如果引用循环包含更多元素,情况可能会复杂得多。所以gc必须检查哪些元素可以从某个根访问,哪些元素被引用,但不可访问,并且应该被收集。

现在,如果你有,在你的代码中的某个地方

Object a=new A(new B(new C(new D())))

构造函数可能需要一段时间才能分配一个。但是你不希望gc删除新创建的D,仅仅因为C的构造函数需要一段时间才能运行,而且a还没有被分配。因此,您还需要扫描年轻的一代,以捕获太年轻而无法从堆中引用的对象。

 类似资料:
  • 那么,我们是否可以仅使用选项运行JVM?我的意思是没有任何为年轻一代描述垃圾收集器的选项。如果我们能那样做,哪一个垃圾收集器将用于老一代?

  • 如果我错了,请随时指正。在JVM堆中,有老一代和年轻一代两代。在做全GC时,在老一代中,有像紧凑空间和修复漏洞这样的繁重操作,这会使JVM挂起。而我发现在年轻一代中,应用了一个轻量级的GC,从我的搜索结果中还有一个叫做Eden的区域涉及年轻一代。但是,在搜索了很多文档后,我对年轻一代中的GC仍然有两个困惑, 在年轻一代中,GC似乎不像老一代GC那样工作(即老一代GC压缩并修复漏洞)?如果是这样,年

  • 我在尝试理解垃圾收集机制,我在研究代际算法,我有一个关于年轻人和老年人的代沟的问题。我读到,在年轻一代开始收集物品,GC是从GC根开始标记它们,以找到活的,通常它会将它们复制到幸存者空间,清除年轻一代区域,然后瞧。 我不明白,如果我们从GC根开始,我们开始遍历活动对象,我们不是也在旧一代中找到了对象吗?这是否意味着,当我们击中旧空间中的一个物体时,我们会在那个点上停止跟踪参照物?

  • 问题内容: 当伊甸园空间年轻时已满,将触发次要GC。在次要GC过程中,伊甸园和一个源Survivor空间中的非自由对象将被复制到另一个目标Survivor空间。 我的问题是,如果目标“幸存者”空间已满,次要GC如何处理? 问题答案: 如果不可能执行/完成次要收集,则将执行主要/完整收集。通常使用标记扫描紧凑算法而不是复制算法来完成此操作……这是完整收集昂贵的原因之一。 但是最终(如果您继续填充堆)

  • 这里有我的GC.log的摘录: 2013-02-28T12:02:13.209+0100:1486457.849:[GC 1486457.850:[ParNew 3483368K->96838K(3774912K),0.2273030秒]8085678K->4710336K(49912256K),0.2278070秒][times:user=1.54 sys=0.01,real=0.23秒] 20

  • 当伊甸园空间充满年轻一代时,小GC将被触发。在次要GC过程中,伊甸园和一个源幸存者空间中的非自由对象将被复制到另一个目标幸存者空间。 我的问题是,如果目标幸存者空间已满,minor GC如何处理?