Java内存中Survivor Space的目的是什么?

dj_*_*rai 5 java garbage-collection jvm memory-management jvm-hotspot

试图寻找这个,但我遇到的所有问题/答案都在谈论拥有 2 个幸存者空间的目的。我想了解一般来说拥有幸存者空间的目的。将物体从伊甸园移动到幸存者有什么好处?

Eug*_*ene 9

性能

一般来说,拆分堆(无论是分代还是任何其他鉴别器)被认为是一件相当好的事情,但并非所有收集器都遵循这一点(Shenandoah例如,不是这样的收集器)。

为什么这是好事?扫描整个堆中的活动对象需要时间。你如何告诉你的垃圾收集器 - “现在就开始运行”。那是什么时候?你可以说:在每 100 个分配的对象之后运行。是不是太快了?(如果这些对象的大小只是堆的一小部分怎么办)或更糟:是否为时已晚?如果你说:在堆占用的 65% 时G1触发一个收集(默认情况下,在该百分比触发一个主要收集)。如果在 65% 时您发现大多数对象应该更早地被收集,它们已经在堆中停留了太多时间,该怎么办?

你可以看到这很快变得复杂。当您了解扫描堆需要时间时,情况会变得更糟,而您最不希望看到的就是应用程序在 GC 运行时停止运行。但也请记住,有一些扫描堆藏concurently,所以他们不会有这样的问题(ShenandoahZGCC4)。

如果可以分离堆,则可以扫描其中的一部分,从而花费很少的时间。人们称它们为“次要”收藏。一些收集器因此将堆划分为“年轻”和“老”,这种分离是基于“婴儿死亡”的前提:年轻的对象死得很快。因此,如果您进行这种分离 + 年轻对象很快就会死亡,您只能扫描堆的特定部分,并且在大多数情况下只处理该部分。这也简化了以下问题的答案:GC 应该何时运行?年轻的时候当然是满的。

现在直接点:为什么需要幸存者,根本。让我们假设它不存在。第一个 GC 循环发生(年轻区域已满,我们Eden确切地说是这样),接下来会发生什么?GC 需要告诉那里有什么是活着的,将它移到“老年代”,清除Eden并重新开始分配。第二个循环进入并做同样的事情,依此类推,直到 GC 说:“如果老年代满了,我就不能再移动了”。这是著名的“老一代”发生的地方。它通常很昂贵。

但我们确实知道这里的“婴儿死亡率”。我们确实知道第二次和第三次 GC 周期将一些对象移动到了在第四阶段收集的老年代。错过了这个机会。因此:幸存者空间。它将对象在那里保留“更长的时间”,然后是一个单一的 GC 周期(称为幸存者年龄),知道在不久的将来这将成为垃圾。因此,不需要经常扫描旧的,只需扫描和处理堆的一部分(EdenSurvivor)。至于为什么有两个幸存者空间,这是一个单独的问题......

实际上,最新的 GC 不需要那个。他们找到了一种方法来扫描堆concurently,您的应用程序运行时,所以他们没有这些空间。年轻死亡的前提仍然存在,一些 GC 算法可能会使用它;现在或将来。

  • @dj_rai_压缩_。如果你这样做了,你的“伊甸园”就会看起来像瑞士象棋:里面有洞。您很快就会遇到无法再分配对象的情况,因为没有连续的空间并且需要进行“碎片整理”。“Survivor”空间通过复制对象来解决这个问题,因此在下一个周期“Eden”(和一个“Survivor”空间)是空的。 (2认同)
  • “老化”策略也可以在单个伊甸园内实施。幸存的对象可以移动到 Eden 空间的开头(就像老一代中的压缩工作方式一样)。但与复制到始终为空的空间相比,实现压缩要困难一些,并且需要更多时间。因此,Survivor 是一种优化,它用一点堆空间来换取更快、更简单的年轻集合。 (2认同)