Shenandoah 垃圾收集器负载参考障碍
Shenandoah Garbage Collector Load Reference Barriers
对于看过 Shenandoah
发展的人来说这不是什么大秘密,一个主要的批评是它为 每个 使用 GC barriers
写入和读取:无论是参考还是原始。
Shenandoah 2.0
声称这不再是问题,它已通过所谓的 加载参考屏障 解决。这到底是怎么回事?
我假设 reader 知道障碍是什么以及为什么需要障碍。非常简短的介绍 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
}
}
我们的想法不是展示一个正确的例子,而是一个正确的例子:
a 阅读 (user.zip;
)
a 写入 (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 activity,屏障 仍然 并且必须有代码 有条件地 检查屏障是否需要执行。
长话短说:无论如何,这些障碍都不是免费的。
需要这些屏障来保持堆一致性,因为在疏散阶段内存中有两个个对象副本,你需要始终如一地阅读和写作。 Consistently 这里意味着在 Shenandoah 1.0
中 read 可能发生在“to-space”或“from-space”(称为“弱 to-space 不变”),而 write 可能发生在 to-space
only.
Shenandoah 2.0
表示它将确保 so-called “to-space 不变”(与之前的 weak 相对)。基本上-它说所有的写入和读取都将发生from/into“to-space”。在疏散期间有两个对象副本:一个在旧区域(称为“from-space”)和一个在新区域(称为“to-space”)。
它通过一个相当简单但绝妙的想法实现了这个“to-space”不变量。它不是在 writes
发生的地方使用障碍,而是确保最初加载的对象肯定是从“to-space”加载的。这是通过 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
屏障并移除了另外两个屏障。因此,load-reference-barriers 发生在加载对象时,他们称之为: 在定义站点 ,而不是在读取或存储到它时,他们称之为 在他们 use-site。您可以将其视为在使用 aload
和 getField
(供参考)的地方插入这些障碍。
对于看过 Shenandoah
发展的人来说这不是什么大秘密,一个主要的批评是它为 每个 使用 GC barriers
写入和读取:无论是参考还是原始。
Shenandoah 2.0
声称这不再是问题,它已通过所谓的 加载参考屏障 解决。这到底是怎么回事?
我假设 reader 知道障碍是什么以及为什么需要障碍。非常简短的介绍 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
}
}
我们的想法不是展示一个正确的例子,而是一个正确的例子:
a 阅读 (
user.zip;
)a 写入 (
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 activity,屏障 仍然 并且必须有代码 有条件地 检查屏障是否需要执行。
长话短说:无论如何,这些障碍都不是免费的。
需要这些屏障来保持堆一致性,因为在疏散阶段内存中有两个个对象副本,你需要始终如一地阅读和写作。 Consistently 这里意味着在 Shenandoah 1.0
中 read 可能发生在“to-space”或“from-space”(称为“弱 to-space 不变”),而 write 可能发生在 to-space
only.
Shenandoah 2.0
表示它将确保 so-called “to-space 不变”(与之前的 weak 相对)。基本上-它说所有的写入和读取都将发生from/into“to-space”。在疏散期间有两个对象副本:一个在旧区域(称为“from-space”)和一个在新区域(称为“to-space”)。
它通过一个相当简单但绝妙的想法实现了这个“to-space”不变量。它不是在 writes
发生的地方使用障碍,而是确保最初加载的对象肯定是从“to-space”加载的。这是通过 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
屏障并移除了另外两个屏障。因此,load-reference-barriers 发生在加载对象时,他们称之为: 在定义站点 ,而不是在读取或存储到它时,他们称之为 在他们 use-site。您可以将其视为在使用 aload
和 getField
(供参考)的地方插入这些障碍。