Bug JDK-8191002 不清楚是编程错误还是JRE错误

Bug JDK-8191002 unclear if programming error or JRE error

关于 JDK-8191002 中描述的问题,也在 中讨论: 我不清楚 finalize() 方法中 Arrays.fill() 的用法是正确的还是错误。一些答案表明应该使用 reachabilityFence,但这是否意味着它是一个错误,或者这是否意味着 reachabilityFence 是 VM 中错误的解决方法? 谁能clarify/comment?

复制自https://docs.oracle.com/javase/specs/jls/se9/html/jls-12.html#jls-12.6:"Furthermore, none of the pre-finalization reads of fields of that object may see writes that occur after finalization of that object is initiated." 这表明 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() 可能确实虚假地 return z 填充数组而不是反映原始内容的数组(理论上,它甚至可以 return 部分填充数组) .

“某字段的读取”是数组reference的读取。数组的克隆(或数组的任何处理)发生在之后,因此,不会阻止字段的所有者被垃圾收集。

由于没有强制执行 inter-thread 内存可见性的操作,因此甚至不需要实际发生这种“读取字段”,线程可以重用先前读取的值(仍在谈论reference here),允许更早地收集对象。如果终结器更改了引用,这仍然满足不感知终结器对字段的写入的要求。

如前所述,这并没有说明数组的内容,因为它不是已被垃圾回收的数组。数组和包含对数组的引用的对象是两个完全不同的对象。

在克隆阵列后在 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.