我有以下的代码片段:
private final List<WeakReference<T>> slaves;
public void updateOrdering() {
// removes void weak references
// and ensures that weak references are not voided
// during subsequent sort
List<T> unwrapped = unwrap();
assert unwrapped.size() == this.slaves.size();
// **** could be reimplemented without using unwrap() ****
Collections.sort(this.slaves, CMP_IDX_SLV);
unwrapped = null;// without this, ....
}
方法
unwrap()
只是创建了一个由slaves
中弱引用所引用的T
列表,同时消除了在slaves
中引用null
的弱引用。然后进行排序,其中依赖于slaves
的每个成员都引用某个T
;否则代码将产生NullPointerException
。由于
unwrapped
在slaves
中拥有每个T
的引用,因此在排序过程中没有GC消除T
。最后,unwrapped = null
消除了对解包的引用,因此再次释放了GC。似乎效果很好。现在我的问题是:如果我删除
unwrapped = null;
,在一些负载下运行许多测试就会导致NullPointerExceptions
。我怀疑JIT消除了List<T> unwrapped = unwrap();
,因此GC在排序期间应用于slaves
中的T
。您是否有另一种解释?如果您同意我的看法,那么这是JIT的一个错误吗?我个人认为不需要
unwrapped = null
,因为unwrapped
一旦updateOrdering()
返回,就会从帧中删除。是否有规范说明可优化的内容和不可优化的内容?还是我做错了?我的想法是修改比较器,使其允许弱引用null
。您对此有何看法?谢谢建议。补充信息(1):
首先是Java版本:
java version "1.7.0_45" OpenJDK Runtime Environment (IcedTea 2.4.3) (suse-8.28.3-x86_64) OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)
然后有人想看unwrap方法。
private synchronized List<T> unwrap() {
List<T> res = new ArrayList<T>();
T cand;
WeakReference<T> slvRef;
Iterator<WeakReference<T>> iter = this.slaves.iterator();
while (iter.hasNext()) {
slvRef = iter.next();
cand = slvRef.get();
if (cand == null) {
iter.remove();
continue;
}
assert cand != null;
res.add(cand);
} // while (iter.hasNext())
return res;
}
请注意,遍历时void引用会被移除。事实上,我已经替换了这个方法。
private synchronized List<T> unwrap() {
List<T> res = new ArrayList<T>();
for (T cand : this) {
assert cand != null;
res.add(cand);
}
return res;
}
我使用了自己的迭代器,但功能上应该是相同的。
有人需要堆栈跟踪信息。这里有一部分。
java.lang.NullPointerException: null
at WeakSlaveCollection$IdxComparator.compare(WeakSlaveCollection.java:44)
at WeakSlaveCollection$IdxComparator.compare(WeakSlaveCollection.java:40)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:324)
at java.util.TimSort.sort(TimSort.java:189)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
at WeakSlaveCollection.updateOrdering(WeakSlaveCollection.java:183)
在比较器中,它指向了带有返回值的那一行。
static class IdxComparator
implements Comparator<WeakReference<? extends XSlaveNumber>> {
public int compare(WeakReference<? extends XSlaveNumber> slv1,
WeakReference<? extends XSlaveNumber> slv2) {
return slv2.get().index()-slv1.get().index();
}
} // class IdxComparator
最后,
private final static IdxComparator CMP_IDX_SLV = new IdxComparator();
是一个重要的常数。
附加内容(2)
现在观察到,即使在updateOrdering()中存在'unwrapped = null',仍然会发生NPE。
如果没有严格的引用保持,在jit优化后,弱引用可能会被Java运行时删除。源代码似乎一点也不重要。
我以下面的方式解决了这个问题:
public void updateOrdering() {
Collections.sort(this.slaves, CMP_IDX_SLV);
}
没有插入任何修饰,以防止从属对象被垃圾回收,并启用CMP_IDX_SLV中的比较器来处理对null的弱引用。
public int compare(WeakReference<? extends XSlaveNumber> slv1,
WeakReference<? extends XSlaveNumber> slv2) {
XSlaveNumber sSlv1 = slv1.get();
XSlaveNumber sSlv2 = slv2.get();
if (sSlv1 == null) {
return sSlv2 == null ? 0 : -1;
}
if (sSlv2 == null) {
return +1;
}
assert sSlv1 != null && sSlv2 != null;
return sSlv2.index()-sSlv1.index();
}
作为一个副作用,对底层列表List> slaves; 进行排序会将空虚的弱引用放置在列表末尾,以便稍后可以回收。
unwrap()
调用本身,但除非之后从未使用过unwrapped
,否则它有资格被收集。无论是空赋值还是不是,它都依赖于垃圾收集器的实现细节。 - kiheruunwrap()
的返回值实际上并没有被使用,并通过不存储它来进行优化(从而完全消除了unwrapped
变量)。无论你尝试什么,你都会在与编译器的斗争中失败。你可以做的是对已拆包的列表进行排序,然后重新打包它们。 - Mattias Buelensunwrap
方法。问题很可能在您的代码中,而不是JIT中。 - user2357112