我知道YGC所花的时间与伊甸园中的活物体数量成正比。我还了解如何在主要集合(线程堆栈中的所有对象和静态对象,以及可从这些对象传递的更多对象)中计算出活动对象。
但我不明白在年轻一代的收藏中是如何弄清楚这些活的物体的?如果它解析线程堆栈,那么它需要解析eden +租期空间,但我认为情况并非如此。那么JVM如何找到伊甸园中的活动对象并将它们复制到To Survivor空间呢?
发布于 2010-10-03 16:09:41
如何在年轻一代的收藏中计算出活的对象?
在this article中可以找到关于如何在HotSpot中实现分代集合的高级描述。
一般来说,分代收集器对年轻一代的标记如下(假设我们只有两代):
在HotSpot中,包含年轻代引用的老代对象使用“卡片表”来标识。老一代被分成512字节的区域,每个区域都有一张“卡”。如果该区域包含任何旧的->新一代指针,则卡中的一个位被设置。然后,在新一代收集期间跟踪设置了Card位的区域中的对象。
棘手的事情是维护Card表,因为新的空间引用被写入到老一代的对象中。在HotSpot中,这是使用软件写屏障实现的,每当将新的空间引用写入与卡对应的存储区时,该写屏障就会设置适当的卡的脏位。正如链接的文章所指出的那样,这使得在对象中设置引用字段的成本更高,但显然是值得的,因为大多数时候只能收集新一代的数据,从而节省了时间。
发布于 2010-10-03 16:05:12
为了只跟踪最年轻的一代,垃圾收集器扫描相同的根集(堆栈和寄存器),以及自上一次年轻一代扫描以来修改过的所有较老(未收集的)代。只有那些已被修改的对象才可能指向年轻一代对象,因为未修改的对象不可能指向在其最后一次修改之后创建的对象。
因此,棘手的部分是,GC如何知道自上次GC以来哪些对象已被修改?可以使用的技术有很多,但它们基本上归结为跟踪对老一代对象的写入。这可以通过捕获写入(写入障碍)或跟踪所有写入目标(写入缓冲区、卡片标记)来完成,所有这些都会在GC未运行时增加程序执行的开销(因此它不会显示为GC暂停时间,但会显示在总运行时间中)。硬件支持很有帮助,如果有的话。跟踪不需要精确,只要扫描每个修改过的老一代对象(扫描未修改的对象是浪费时间,但不会造成任何伤害)。
发布于 2010-10-03 15:46:56
我在这里引用了article by Brian Goetz中的相关文本。
跟踪垃圾收集器,如复制、标记-清除和标记-压缩,都从根集开始扫描,遍历对象之间的引用,直到所有活动对象都被访问。分代跟踪收集器从根集开始,但不会遍历指向前一代中的对象的引用,这会减小要跟踪的对象图的大小。
https://stackoverflow.com/questions/3848830
复制相似问题