【发布时间】:2020-09-20 03:04:16
【问题描述】:
对于看过Shenandoah 发展的人来说,一个主要的批评是它使用GC barriers 来每一个读写:无论是参考还是原始,这并不是什么大秘密.
Shenandoah 2.0 声称这不再是问题,并且通过所谓的加载引用障碍解决了。这到底是怎么回事?
【问题讨论】:
标签: java garbage-collection jvm shenandoah
对于看过Shenandoah 发展的人来说,一个主要的批评是它使用GC barriers 来每一个读写:无论是参考还是原始,这并不是什么大秘密.
Shenandoah 2.0 声称这不再是问题,并且通过所谓的加载引用障碍解决了。这到底是怎么回事?
【问题讨论】:
标签: java garbage-collection jvm shenandoah
我假设读者知道什么是障碍以及为什么需要它。非常简短的介绍here is another answer of mine on the topic。
为了正确理解这一点,我们需要首先看看最初的问题到底出在哪里。举个比较简单的例子:
static class User {
private int zip;
private int age;
}
static class Holder {
private User user;
// other fields we don't care about
}
现在让我们想象一下这样的理论方法:
public void access(Holder holder){
User user = holder.user;
for(;;){ // some loop here
int zip = user.zip;
System.out.println(zip);
user.age = // some value taken from the loop for example
}
}
我们的想法不是展示一个正确的例子,而是一个可以做到的例子:
一个读取 (user.zip;)
一个写 (user.age = ...)
现在因为Shenandoah 1.0 需要在任何地方引入障碍,这段代码看起来会是:
public void access(Holder holder){
User user = RB(holder).user;
for(;;){ // some loop here
int zip = RB(user).zip;
System.out.println(zip);
WB(user).age = // some value taken from the loop for example
}
}
注意RB(holder).user(RB 代表read barrier)和WB(user).age(WB 代表write barrier)。现在想象一下循环是hot——你将为这么多障碍付出代价。即使在循环执行期间没有 GC 活动,屏障仍然就位,并且必须有代码有条件地检查是否需要执行屏障与否。
长话短说:这些障碍无论如何都不是免费的。
这些屏障是维持堆一致性所必需的,因为在疏散阶段内存中有一个对象的两个副本,因此您需要始终保持一致的读取和写入。 一致在这里意味着在Shenandoah 1.0中,读取可能发生在“to-space”或“from-space”(称为“weak to-space invariant”) ,而 write 可能发生在 to-space only。
Shenandoah 2.0 表示它将确保所谓的“空间不变”(与之前的 weak 相对)。基本上 - 它说所有的写入和读取都将发生在/进入“to-space”。在疏散期间,对象有两份副本:一份在旧区域(称为“从空间”),另一份在新区域(称为“到空间”)。
它通过一个相当简单但绝妙的想法实现了这个“to-space”不变量。它不是在writes 发生的地方使用障碍,而是确保最初加载的对象肯定是从“到空间”加载的。这是通过 load-reference-barriers 完成的。通过重构前面的示例,这更容易理解:
public void access(Holder holder){
User user = LRB(holder).user;
for(;;){ // some loop here
int zip = user.zip;
System.out.println(zip);
user.age = // some value taken from the loop for example
}
}
我们引入了LRB 屏障并移除了另外两个。因此,加载引用障碍会在加载对象时发生,它们在定义站点调用此:在定义站点,而不是在读取或存储到它时,他们在其使用站点调用此 。您可以将其视为在使用aload 和getField(供参考)的位置插入这些障碍。
【讨论】: