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.
关于 JDK-8191002 中描述的问题,也在
复制自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.