Java VM是否在内存中移动对象,如果是这样 - 如何?

时间:2008-09-18 00:13:58

标签: java memory-management garbage-collection jvm

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

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

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

  1. 在对象的生命周期内保持不移动的引用间接,如果对象移动,我们会更新该对象。但是 - 如何管理这些间接?
  2. 保留每个对象的反向引用列表,因此我们知道如果移动对象需要更新的内容。当然,这会产生性能开销。
  3. 我对这些方法的反馈以及对替代方法的任何建议感兴趣。

6 个答案:

答案 0 :(得分:11)

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

不同的GC会采用不同的方式。

通常在收集器走到堆时复制收集器,它们不会遍历堆中的所有对象。而是他们在堆中走LIVE对象。这意味着如果它可以从“根”对象到达,那么该对象就是实时的。

因此,在此阶段必须触摸所有活动对象,因为它将它们从旧堆复制到新堆。完成活动对象的副本后,旧堆中剩余的所有内容都是已复制的对象或垃圾。此时,旧堆可以完全丢弃。

这种收集器的两个主要好处是它在复制阶段压缩堆,并且它只复制活动对象。这对许多系统来说很重要,因为使用这种收集器,对象分配很便宜,实际上只是增加堆指针。当GC发生时,没有任何“死”对象被复制,因此它们不会减慢收集器的速度。在动态系统中也发现,与长期存在的垃圾相比,存在更多的小型临时垃圾。

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

这不是讨论GC机制的论坛,因为它是一个非常重要的问题,但这是复制收集器如何工作的基础。

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

答案 1 :(得分:3)

(实际上)任何垃圾收集系统都必须在内存中移动对象以更密集地打包它们并避免碎片问题。

你所看到的是一个非常庞大而复杂的主题。我建议您阅读现有的远程对象样式API:.NET远程处理和更进一步的技术,如CORBA

通过必须处理分布式系统中存在的所有故障模式,任何跟踪引用的解决方案都将变得复杂。 JVM不必担心突然发现它无法看到它的一半堆,因为网络交换机出现了故障。

当您深入研究设计时,我认为很多问题将归结为您希望如何处理不同的故障情况。

对评论的回应:

您的问题涉及以分布式方式存储对象,这正是.NET远程处理和CORBA解决的问题。不可否认,这两种技术都不支持这些对象的迁移(AFAIK)。但它们都广泛涉及对象身份的概念,这是任何分布式对象系统的关键部分:系统的不同部分如何知道他们正在谈论的对象。

我并不过分熟悉Java垃圾收集器的细节,我确信Java和.NET垃圾收集器在它们中具有很多复杂性,以实现最大性能,同时对应用程序的影响最小。

然而,垃圾收集的基本思路是:

  • VM阻止所有线程运行托管代码
  • 它从已知的“根”集执行可达性分析:静态变量,所有线程上的局部变量。对于每个对象,它会在对象中的所有引用之后找到它。
  • 可达性分析未识别的任何对象都是垃圾。
  • 然后,仍然活着的对象可以在内存中向下移动以密集打包它们。这意味着对这些对象的任何引用也必须使用新地址进行更新。通过控制何时可以发生垃圾收集,VM能够保证在空中没有对象引用(即,保存在机器寄存器中),这将引起问题。
  • 一旦完成该过程,VM将再次启动线程执行。

作为对此过程的改进,VM可以执行分代垃圾收集,其中基于对象的“年龄”维护单独的堆。对象从堆0开始,如果它们在几个GC中存活,则迁移到堆1并最终迁移到堆2(依此类推 - .NET仅支持3代)。这样做的好处是GC可以非常频繁地运行堆0集合,而不必担心要做的工作来证明长寿命对象(最终在堆2中)仍然存在(它们几乎肯定是)

还有其他一些改进可以支持并发垃圾收集,并且在计划GC时实际执行非托管代码的线程的详细信息会给这个区域增加更多的复杂性。

答案 2 :(得分:3)

我很想知道你的要求。正如另一个答案所示,Terracotta可能正是您所寻找的。

然而,Terracotta提供的内容与您要求的内容之间存在细微差别,因此我的询问也是如此。

不同之处在于,就您而言,Terracotta不提供对象的“远程”引用 - 事实上,使用Terracotta时完全没有RMI,JMS等的整个“远程”概念。

相反,在Terracotta中,所有对象都驻留在大型虚拟堆中。无论是在节点1上,还是节点2,节点3,节点4等,线程都可以访问虚拟堆中的任何对象。

没有特殊的编程需要学习,或者特殊的API,“虚拟”堆中的对象与本地堆中的对象具有完全相同的行为。

简而言之,Terracotta提供的是多个JVM的编程模型,其运行方式与单个JVM的编程模型完全相同。单独节点中的线程只是表现为单个节点中的线程 - 对象突变,同步,等待,通知所有节点在节点上的行为与跨线程完全相同 - 没有区别。

此外,与之前的任何解决方案不同,对象引用是跨节点维护的 - 这意味着您可以使用==。这是在整个集群中维护Java内存模型的一部分,这是制作“常规”Java(例如POJO,同步,等待/通知)工作的基本要求(如果您不能/不能保留,那么这些工作都不起作用整个集群中的对象标识。)

所以回答你的问题是进一步完善你的要求 - 你需要什么目的“远程”指针?

答案 3 :(得分:2)

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

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

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

答案 4 :(得分:1)

听起来你正在寻找分布式缓存,比如terracotta或oracle的java objece缓存(以前称为tangersol)。

答案 5 :(得分:0)

如果您愿意深入了解,可以查看JBoss Cache架构文档,并获取一些源代码作为参考。

这不完全是您所描述的,但它的工作方式非常相似。

这是链接。

http://www.jboss.org/jbosscache/

我希望这会有所帮助。