Java VM 是否移动内存中的对象,如果是,如何移动?

Java 虚拟机是否曾经移动过内存中的对象,如果是,它如何处理更新对移动对象的引用?

我问是因为我正在探索以分布式方式(即跨多个服务器)存储对象的想法,但是出于效率原因,我需要能够在服务器之间移动对象。对象需要能够包含指向彼此的指针,甚至指向远程服务器上的对象。我正在尝试思考更新对移动对象的引用的最佳方法。

到目前为止,我的两个想法是:

  1. 在对象的生存期内不移动的某个位置维护一个引用间接寻址,如果对象移动,我们会更新该间接寻址。但是 - 如何管理这些间接层?
  2. 为每个对象保留一个反向引用的列表,以便我们知道在移动对象时必须更新的内容。当然,这会产生性能开销。

我对这些方法的反馈以及任何替代方法的建议很感兴趣。


答案 1

参考上面关于在堆中行走的评论。

不同的GC以不同的方式做到这一点。

通常,当收集器遍历堆时,它们不会遍历堆中的所有对象。相反,它们在堆中行走LIVE对象。这意味着,如果可以从“根”对象访问它,则该对象是实时的。

因此,在此阶段,无论如何都必须触摸所有活动对象,因为它将它们从旧堆复制到新堆。一旦活动对象的副本完成,旧堆中保留的所有内容要么是已复制的对象,要么是垃圾。此时,可以完全丢弃旧堆。

这种收集器的两个主要优点是,它在复制阶段压缩堆,并且只复制活对象。这对许多系统来说很重要,因为对于这种收集器,对象分配非常便宜,实际上只不过是增加堆指针。当 GC 发生时,不会复制任何“死”对象,因此它们不会降低收集器的速度。在动态系统中,也证明,临时垃圾比长期存在的垃圾要少得多。

此外,通过遍历实时对象图,您可以看到 GC 如何“了解”每个对象,并跟踪它们以用于复制期间执行的任何地址调整目的。

这不是深入讨论GC机制的论坛,因为这是一个不平凡的问题,但这是复制收集器如何工作的基本知识。

代复制 GC 会将“较旧”的对象放在不同的堆中,并且这些对象最终被收集的频率低于“较新”的堆。该理论认为,持久的物体被提升到老一辈,并且被收集得越来越少,从而提高了GC的整体性能。


答案 2

您追求的关键词是“压缩垃圾回收器”。允许 JVM 使用一个,这意味着对象可以重新定位。请查阅 JVM 的手册,了解 JVM 的手册是否确实如此,并查看是否有任何命令行选项会影响它。

解释压缩的概念上最简单的方法是假设垃圾回收器冻结所有线程,重新定位对象,在堆和堆栈中搜索对该对象的所有引用,并使用新地址更新它们。实际上,它比这更复杂,因为出于性能原因,您不希望在线程停止的情况下执行完全扫描,因此增量垃圾回收器将尽可能地为压缩做准备。

如果您对间接引用感兴趣,则可以从研究Java中的弱引用和软引用以及各种RPC系统使用的远程引用开始。