关于JDK-8191002中描述的问题,也在Java Cipher - PBE thread-safety issue中讨论过: 我不清楚在finalize()方法中Arrays.fill()的使用是否正确或是否是一个bug。一些答案表明应该使用reachabilityFence,但这是否意味着它是一个错误,或者这是否意味着reachabilityFence是针对VM中的错误的解决方法? 有谁可以澄清/评论?
从https://docs.oracle.com/javase/specs/jls/se9/html/jls-12.html#jls-12.6复制:"此外,该对象的字段的预定义读取都不会看到在启动该对象的最终化之后发生的写入。" 这表明JDK-8191002中NewlyAllocatedArrayFilledByOtherInstanceFinalizer的代码是正确的,并且该故障是由于JVM引起的。或不?
答案 0 :(得分:1)
简而言之,这是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
填充数组而不是反映原始内容的数组(理论上,它甚至可以返回部分填充的数组)。
“读取字段”是读取数组引用。克隆数组(或数组的任何处理)发生之后,因此,不会阻止字段的所有者被垃圾收集。
由于没有强制执行线程内存可见性的操作,因此甚至不需要实际发生这种“字段读取”,该线程可能会重用先前读取的值(仍然在讨论的值)参考这里),允许更早的对象集合。如果终结器改变了引用,这仍然要求不要感知终结器对字段的写入。
如上所述,这并没有说明数组的内容,因为它不是垃圾收集的数组。数组和包含对数组的引用的对象是两个完全不同的对象。
在克隆数组后在holder上放置一个可访问性围栏会创建一个新的依赖项,而不是在数组克隆完成之前无法收集数组持有者。
byte[] getBytes() {
byte[] result = _bytes.clone();
Reference.reachabilityFence(this);
return result;
}
没有它,在调用clone()
之前,对象的最后一次访问是,但正如所说的,通过重用先前读取的引用可以优化该访问。如JLS§12.6.1。所述:
可以设计优化程序的转换,以减少可达到的对象数量,使其少于可以被认为可达的对象数量。