11

在收集时,垃圾收集器将所有活动对象复制到另一个内存空间,从而丢弃进程中的所有垃圾对象。指向新空间中复制对象的前向指针安装到对象的“旧”版本中,以确保收集器正确更新对该对象的所有剩余引用,并且不会错误地将同一对象复制两次。

这显然适用于停止世界的收藏家。然而,由于 stop-the-world 的暂停时间很长,现在大多数垃圾收集器允许 mutator 线程与收集器并发运行,只在短时间内停止 mutator 以进行初始堆栈扫描。

那么收集器如何确保在复制对象时/之后不会访问对象的“旧”版本?我想变异器可以检查带有某种读取障碍的前向指针,但这对我来说似乎代价高昂,因为变量被经常读取。

4

3 回答 3

3

在 Azul 的 Generational Pauseless Garbage Collector 中实现的Loaded Value Barrier是解决此问题的一个示例。你可以在 2011 年初在 InfoQ 上发布的文章The Azul Garbage Collector中了解它。

于 2013-01-21T09:44:53.170 回答
2

您几乎需要使用读屏障或写屏障。您显然已经意识到阅读障碍,所以我不会尝试进入它们。

写入器屏障起作用,因为只要您阻止写入发生,您根本不关心是否有人访问旧的或新的数据副本。您设置写屏障,复制数据,然后开始调整指针。复制完成后,您并不真正关心是否有人读取数据的旧副本或新副本,因为写入屏障确保它们是相同的。一旦你完成了指针的调整,一切都将与新数据一起工作,所以你撤销了写屏障。

使用页面保护位将内存区域标记为只读以在相当标准的硬件上创建写屏障已经完成了一些工作。然而,至少在我最后一次研究它时,这仍处于概念验证阶段——工作,但速度太慢,不太实用。

于 2013-01-21T17:29:11.780 回答
2

免责声明:这仅与java有关Shenandoah GC

你的理由绝对正确Shenandoah!例如这里的一些细节。

在不久前,所有原始类型和引用类型Shenandoah都有写入和读取障碍。正如您所假设的那样,读取障碍实际上只是通过的单一间接。由于它们与写入(这是一个更复杂的障碍)相比要多得多,因此这些读取障碍比那些读取障碍更昂贵(累积时间)。这与这些数量如此之多的唯一事实有关。forwarding pointerwrite

但是,当实施屏障时, jdk-13 中的情况发生了变化。Load Reference因此,只有负载有障碍,写入以通常的方式发生。如果您考虑一下,这很有意义,为了写入Object 字段,您需要先读取该对象,因此如果您的屏障保留“to-space 不变量”,您将始终从最近的和对象的正确副本;无需使用带有forwarding pointer.

于 2019-12-02T15:04:34.783 回答