如果JVM在执行GC时继续移动对象,它如何解析引用?

我正在阅读JVM调优,我发现JVM在执行GC时会不断移动对象。 但Java对象之间有相互引用,人们认为它们是作为指针实现的,但每次移动对象后JVM都不可能遍历整个堆,并更新所有引用; 肯定会永远。 那么它如何解析引用,如果引用没有改变,但对象的物理位置呢?

我已经阅读了很多关于JVM的内容,但在任何地方都没有解释,甚至暗示过。

[编辑]我的观点是,引用是单向的。 从指针指向指向是“瞬时”,但反过来需要完整的堆扫描。 虽然有可能,但似乎不太可能。 如果10K对象在次要集合中存活,那么执行完整堆扫描需要多长时间才能更新对这些对象的引用10K次? 必须使用某种优化的算法或结构。

如果你真的对垃圾收集者的工作方式感兴趣,我可以推荐Richard Jones的2本关于垃圾收集的书。 链接/参考在这里 。 这不是关于Java垃圾收集的具体内容。

(我有一本旧书的副本,新书在我的购物清单上。)


这是复制收集器如何处理此问题的简单版本。

复制收集器通过将对象从一个空间(从空间)复制到另一个空间(到空间)来工作。

具体来说,GC从每个GC根开始遍历“from”空间内的可到达对象的图形。 每次找到对节点的引用时(在实例字段,静态字段,堆栈帧等中),它都会检查引用指向的对象,以查看它是否已标记为已访问。

  • 如果尚未标记,GC将执行以下操作:

    1. 它标志着来自太空的物体。
    2. 它将对象复制到to-space。
    3. 它将对象的地址存储在from-space对象的空间中。 (这就像转发地址。)
    4. 它以递归方式访问对象的to-space副本的每个引用字段。

    这是对to-space对象的引用的结果。

  • 如果已经标记了对象,则GC会查找转发地址,然后返回该地址。

GC获取引用的位置(在空间中,或某些GC根目录)然后使用指向空间中对象的指针进行更新。

如果您遵循所有这些,那么您将看到GC不需要查找包含对给定移动对象的引用的所有位置。 相反,它只是遇到遍历可到达对象的所有位置。 当然,GC 确实必须进行遍历,但是有各种技术可以减少每个GC循环中需要完成的遍历量。

如果您没有按照上述内容进行操作,那么请阅读我推荐的其中一本教科书。 他们在解释它方面做得比我能做得好得多。 您还可以找到有关其他类型的GC如何处理此问题的材料。


Java HotSpot GC都是一种或另一种forms的复制收集器。 对于并行和并发收集,事情比我上面描述的要复杂得多,但“转发地址”机制对所有这些都是通用的。

(HotSpot GC上发表的论文或其他公共文档并不多,而且大多数存在的材料都假定读者对现代垃圾收集器的工作原理有很好的理解。)

每次移动对象后,JVM都不可能遍历整个堆,并更新所有引用

我自己并不是GC的专家,但据我所知,这或多或少都是它的作用。 参见例如本文:

相反,复制收集器在到达时将可到达对象复制到另一个内存区域。 […]在这样的遍历之后,所有幸存的对象都驻留在连续的内存区域中, 并且所有指针都已更新为指向新的对象位置 。 […]在此过程中,GC会构建一个对象图来跟踪“实时”对象,以便它可以更新对其移动的任何对象的引用。

http://wiki.osdev.org/Garbage_collection#Copy_collectors ,强调我的)。

至于“永远不要” – 复制(或移动)垃圾收集器背后的主要思想是实际上只需要移动少量对象,因为大多数实例已经死了(即大多数实例都很短) -lived)。 因此移动的对象数量很少,并且希望指向它们的引用数量也相当小。

无论如何,GC必须构建一个对象引用列表(以找出哪些对象仍被引用/存活并需要复制),因此它可能会重用该列表来更新引用。 因此,唯一的更新是“额外工作”。

我并不完全确定这是管理中对象引用的方式,但我怀疑Java VM分配给我们程序的对象引用不是实际的内存地址,而是指向实际地址的内部JVM引用在JVM(HashMap或类似结构)中。 即所有引用objectA的对象都会向objectA引用[NOT address],当GC发生时,JVM不需要更新所有这些对象中的引用,只需更新它自己的HashMap中的实际更改地址。

每次移动对象后,JVM都不可能遍历整个堆,并更新所有引用; 肯定会永远

它肯定会扫描整个堆以检测任何人不再引用的对象,并将它们标记为有资格被收集,并将所有活动对象放在紧凑的内存区域中以避免碎片。

它是如何做的取决于所使用的垃圾收集算法,但它确实是一个耗时的过程,这也是Java(本身)不能用于实时约束的原因

通常,collections家不会走遍整个堆。 它们识别活动对象并遍历它们。

例如,Hotspot中的复制收集器以root开头并标识所有活动对象。 识别实时对象后,它们将被复制到堆上的新空间。 在遍历所有活动对象时,它会对活动对象执行所需的地址修改。

完成此操作后,旧空间中剩下的所有内容都是死对象和已移动的对象。 此可用空间由GC回收,并在将来用于将其他活动对象移入其中。

所花费的时间与堆上的活动对象的数量成比例。