8

我正在研究创建一个具有最终字段的不可变数据类型(包括在分配给最终成员字段之前构造和填充的数组),并注意到似乎指定 JVM 以保证任何其他获得的线程对该对象的引用将看到已初始化的字段和数组值(假设this在构造函数中没有发布指向的指针,请参阅什么是“未完全构造的对象”?以及链接构造函数时 JVM 的隐式内存屏障如何表现?)。

我很好奇这是如何在不同步对该对象的每次访问或以其他方式支付一些重大性能损失的情况下实现的。据我了解,JVM可以通过以下方式实现:

  1. 在构造函数的末尾发出写栅栏
  2. 仅在写栅栏之后发布对新对象的引用
  3. 每当您引用对象的最终字段时发出阅读围栏

我想不出一种更简单或更便宜的方法来消除其他线程看到未初始化的最终字段(或通过最终字段的递归引用)的风险。

这似乎会由于读取对象的其他线程中的所有读取栅栏而造成严重的性能损失,但消除读取栅栏会引入对象引用在另一个处理器发出读取之前被看到的可能性。 fence 或以其他方式查看与新初始化的最终字段相对应的内存位置的更新。

有谁知道这是如何工作的?这是否会带来显着的性能损失?

4

1 回答 1

4

请参阅本文中的“内存屏障”部分。

在设置最终字段之后和将对象引用分配给另一个变量之前,需要StoreStore屏障。这是您要询问的关键信息。

根据那里的“重新排序”部分,最终字段的存储不能相对于对包含最终字段的对象的引用的存储进行重新排序。

此外,它指出在 中v.afield = 1; x.finalField = v; ... ; sharedRef = x;,前两个都不能相对于第三个重新排序;这确保了在存储对包含最终字段的对象的引用之前,对存储为最终字段的对象的字段的存储本身保证对其他线程可见。

总之,这意味着在存储对包含该字段的对象的引用之前,所有线程都必须对所有线程可见对最终字段的所有存储。

于 2013-08-08T03:38:46.717 回答