为什么JVM在内存中重定位对象?

时间:2013-08-31 17:07:56

标签: java memory-management garbage-collection jvm

现代JVM有时会在垃圾回收期间重新定位内存中的对象,即使System.identityHashCode(object)的值在对象的生命周期内保持不变。我不明白为什么JVM在内存中重定位对象?

2 个答案:

答案 0 :(得分:3)

如果内存碎片化(即中间的位和部分被删除),则可以通过对其进行碎片整理来提高内存访问速度。此外,对于大型对象,对其进行碎片整理可以打开更大的内存。

为了清晰起见编辑:如下面的评论中所述,垃圾收集算法通常会保留内存的“区域”。有些是持久的,有些则不是。该算法在不同的JVM上有所不同,但它通常包括将对象从一个区域移动到另一个区域,以便完全清空区域(如果它已经几乎为空)。然后可以将该区域作为一个整体提供。并且JVM非常聪明地发现哪些对象意味着持久而哪些不是,但有时它必须调整其策略并重新定位对象。

答案 1 :(得分:1)

  

我不明白为什么JVM会在内存中重定位对象?

对于后人,我认为在这里总结一些对其他答案的评论是好的。 @assylias应该写这个,但他听起来很忙。正如@Ghostkeeper指出的那样,可重定位对象的一个​​重要好处是降低(或消除)内存碎片中固有的问题。较长的较小的对象可以移动到相似大小的其他对象旁边,以打开连续的自由空间供大型对象使用。

但是,对象重定位如此重要的一个更重要的原因是内存和垃圾收集性能。大多数现代JVM实现了generational garbage-collector,它维护了许多不同的内存池。通过使用jconsole并连接到正在运行的应用程序,通常可以看到这些池。有关详细信息,请参阅此答案:How is the java memory pool divided?

短期对象进入一个池,高性能GC算法可以在它们上运行,因为很多对象会立即被收集以供重用。通过使用有关池的信息,GC可以轻松确定无法访问对象而无需遍历整个参考树。例如,如果您有如下所示的日志行,则会创建隐含的StringBuilder和生成的String,然后可以立即从短期对象池中收集:

logger.info("automation run took " + diffMillis + "ms");
// when we reach this line, the StringBuilder and String are no longer in use

由于对象是可重定位的,当高性能GC算法在短期池中找到仍在使用的对象时,会将其复制(重定位)到另一个池,并使用新位置更新对象的引用。然后JVM有一个干净的短期内存池,它可以再次开始填充。使用运行频率较低的更昂贵的GC算法(mark-and-sweep)收集或重新定位中期或长期内存池中的对象。根据对象所在的池以及能够在池之间迁移对象,这种利用不同GC算法的能力是当今JVM中现代高性能GC系统的关键部分。

因此,可重定位对象的最终目标是整体内存性能和碎片整理。