Java垃圾回收:为什么需要两个Survivor空间?

12

在大多数在线教程中,我们可以看到它们显示GC中有两个存活空间。

当我们实际上只需要一个时,为什么需要有两个存活空间呢?

拥有两个存活空间对性能有什么影响?


这可能会对你有所帮助。https://dev59.com/rGgv5IYBdhLWcg3wTvE- - Grmpfhmbl
1个回答

28

原因很简单,你猜对了,就是性能。首先让我解释为什么幸存者空间存在。就 对象重定位 这个关键问题而言,垃圾收集器有两种主要设计:

  • 原地压缩收集器;

  • 复制收集器。

复制收集器可以更快地运行,并且可以高效地并行处理,基本上是因为它从不覆盖任何对象。为了实现这一点,它必须使用一个“活动”的堆空间和一个“休眠”的堆空间,这两个空间在每次GC运行后交换角色。

复制收集器的基本设计

注意:下面的内容并不是HotSpot实际的GC描述,而是C.J. Cheney于1970年在ACM论文中介绍的原始设计的组成部分。HotSpot增加了其他的改进,其中一个是下面解释的伊甸园空间。

当复制收集过程开始时,涉及到两个空间:

  • From 空间:一个连续的内存块,分成两个区域:
    • 上次GC后的幸存者;
    • 新对象(自上次GC以来创建);
  • To 空间:完全空的空间。

GC运行的任务是识别From空间中的所有幸存对象,并将它们复制到To空间中。

随着To空间被构建起来,From空间将完全清空,不需要对其进行任何写入操作。

复制GC是单次遍历

复制收集器的一个关键优点是它是单次遍历的:我们只需扫描所有GC根对象,复制所有这些对象,然后扫描这些对象以获取更多的引用,复制所有的引用。我们永远不会重新访问任何对象,也不需要任何支持内存结构。(参见原文)

此处有一个很好的解释。

向前指针

当每个幸存对象被重新定位时,旧位置可以用一个向前指针标记,并且可以高效地将该信息用于扫描幸存对象以查找对已重新定位对象的引用,并更新这些引用。旧引用指向向前指针,因此新的指针值只需要一个查找即可。

为什么需要第三个空间?

你的问题,“为什么有两个幸存者空间?”实际上更好的表达应该是,“为什么需要一个单独的伊甸园空间?”。

HotSpot引入了伊甸园空间作为优化措施,保持新分配区域的容量不变,赌注在大部分对象会立即变成垃圾的结果上。你可以把伊甸园看作是两个空间之间共享的一部分内存——下一个GC可能会释放的内存部分。这实际上提高了内存利用率。


“然后扫描这些对象以获取进一步的引用,并复制所有引用。”我不太理解这句话。例如,如果两个对象相互引用,那么上述过程可能永远不会结束?或者更糟糕的是,导致重复/无限的对象。他们如何处理这个问题? - du369
1
转发指针标记对象已经移动。 - Marko Topolnik
@du369 这是一个简单的无限循环预防方法,你只需要将对象标记为已处理(或正在处理),不再进行处理。 - maaartinus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接