关于 JDK-8191002 中描述的问题,也在 Java Cipher - PBE thread-safety issue 中进行了讨论: 我不清楚 finalize() 方法中 Arrays.fill() 的用法是正确的还是错误。一些答案表明应该使用 reachabilityFence,但这是否意味着它是一个错误,或者这是否意味着 reachabilityFence 是 VM 中错误的解决方法? 谁能澄清/评论?
复制自https://docs.oracle.com/javase/specs/jls/se9/html/jls-12.html#jls-12.6 :“此外,该对象字段的预终结读取都不会看到在该对象的终结启动后发生的写入。” 这表明 JDK-8191002 中 NewlyAllocatedArrayFilledByOtherInstanceFinalizer 的代码是正确的,失败是由于 JVM。或不?
最佳答案
简而言之,这是 Java 代码中的错误,而不是 JVM 中的错误。
此代码模式已在 JDK-8191002 中抽象出来与
static class ArrayHolder
{
private byte[] _bytes;
ArrayHolder(final byte[] bytes) { _bytes = bytes.clone(); }
byte[] getBytes() { return _bytes.clone(); }
@Override
protected void finalize() throws Throwable
{
if (_bytes != null)
{
Arrays.fill(_bytes, (byte) 'z');
_bytes = null;
}
super.finalize();
}
}
其中 getBytes()
可能确实会虚假地返回 z
填充的数组,而不是反射(reflect)原始内容的数组(理论上,它甚至可以返回部分填充的数组)。
“读取一个字段”是数组reference的读取。数组的克隆(或数组的任何处理)发生在之后,因此,不会阻止该字段的所有者被垃圾收集。
由于没有强制执行线程间内存可见性的操作,因此甚至不需要实际发生这种“读取字段”,线程可以重用先前读取的值(仍在谈论 的值在这里引用),允许更早地收集对象。如果终结器更改了引用,这仍然满足不感知终结器对字段的写入的要求。
如前所述,这并没有说明数组的内容,因为它不是已被垃圾回收的数组。数组和包含数组引用的对象是两个完全不同的对象。
在克隆数组后在 holder 上放置可达性栅栏会创建一个新的依赖关系,该依赖关系不存在,因为在完成数组克隆之前无法收集数组 holder。
byte[] getBytes() {
byte[] result = _bytes.clone();
Reference.reachabilityFence(this);
return result;
}
如果没有它,对对象的最后一次访问之前 clone()
的调用,但如前所述,该访问可能会通过重用先前读取的引用。正如 JLS §12.6.1 所述:
Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable.
关于java - Bug JDK-8191002 不清楚是编程错误还是 JRE 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49171990/