首页 > 解决方案 > Does garbage collection change the object addresses in Java?

问题描述

I read that garbage collection can lead to memory fragmentation problem at run-time. To solve this problem, compacting is done by the JVM where it takes all the active objects and assigns them contiguous memory. This means that the object addresses must change from time to time? Also, if this happens,

  1. Are the references to these objects also re-assigned?
  2. Won't this cause significant performance issues? How does Java cope with it?

标签: javaperformancememory-managementgarbage-collectionjvm

解决方案


我读到垃圾收集会在运行时导致内存碎片问题。

这不是垃圾收集堆的独有问题。当您有一个手动管理的堆并以与前面分配不同的顺序释放内存时,您也可能会得到一个碎片堆。并且能够具有与自动存储(即堆栈内存)的后进先出顺序不同的生命周期,是使用堆内存的主要动机之一。

为了解决这个问题,压缩由 JVM 完成,它获取所有活动对象并为它们分配连续的内存。

不一定是所有对象。典型的实现策略会将内存划分为逻辑区域,并且只将对象从特定区域移动到另一个区域,而不是一次移动所有现有对象。这些策略可能会结合对象的年龄,比如分代收集器将年轻一代的对象从 Eden 空间移动到 Survivor 空间,或者剩余对象的分布,比如“Garbage First”收集器,顾名思义,首先疏散垃圾率最高的片段,这意味着获得空闲连续内存块的工作量最小。

这是否意味着对象地址必须不时更改?

当然,是的。

此外,如果发生这种情况,

  1. 对这些对象的引用是否也重新分配了?

该规范没有规定如何实现对象引用。间接指针可以消除调整所有引用的需要,另请参阅此 Q&A。然而,对于使用直接指针的 JVM,这确实意味着这些指针需要适应。

  1. 这不会导致严重的性能问题吗?Java 是如何应对的?

首先,我们必须考虑从中获得什么。“消除碎片化”本身并不是目的。如果我们不这样做,我们必须扫描可达对象之间的间隙,并创建一个数据结构来维护这些信息,我们称之为“空闲内存”。我们还需要实现内存分配,以在此数据结构中搜索匹配的块,或者在没有找到精确匹配的情况下拆分块。与从连续的空闲内存块进行分配相比,这是一个相当昂贵的操作,我们只需将指针指向下一个空闲字节所需的大小。

鉴于分配比垃圾收集更频繁地发生,垃圾收集仅在内存已满(或已超过阈值)时运行,这确实证明了更昂贵的复制操作是合理的。这也意味着只使用更大的堆可以解决性能问题,因为它减少了所需的垃圾收集器运行的数量,而幸存对象的数量不会随着内存而扩展(无法访问的对象保持无法访问,无论您延迟多长时间收藏)。事实上,推迟收集会增加更多对象同时变得无法访问的机会。也与这个答案比较。

调整引用的成本并不比在标记阶段遍历引用的成本高多少。事实上,非并发收集器甚至可以将这两个步骤结合起来,在第一次遇到时传输对象并调整随后遇到的引用,而不是标记对象。实际的复制是更昂贵的方面,但如上所述,通过不复制所有对象而是使用基于典型应用程序行为的某些策略(如分代方法或“垃圾优先”策略)来减少所需的工作,从而减少成本。


推荐阅读