我有以下代码:
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
的列表并且作为副作用消除了CCD_ 5中引用CCD_。然后是依赖于slaves
的每个成员引用某个T
的排序;否则该代码产生CCD_ 8。
由于unwrapped
在slaves
中的每个T
上保持一个引用,因此在排序期间,没有GC消除T
。最后,unwrapped = null
消除了展开时的引用因此再次释放GC。似乎工作得很好。
现在我的问题是:
如果我删除unwrapped = null;
,当在一定负载下运行许多测试时,这将导致NullPointerExceptions
。我怀疑JIT消除了List<T> unwrapped = unwrap();
因此GC在排序期间应用于CCD_ 17的从机中。
你还有其他解释吗?如果你同意我的观点,这是JIT中的一个错误吗?
我个人认为unwrapped = null
不应该是必要的,因为一旦updateOrdering()
返回,unwrapped
就会从帧中删除。有没有一个可以优化的规范和没有优化的规范?
还是我做错了?我有修改比较器的想法,它允许null
上的弱引用。你觉得怎么样?
谢谢你的建议。
加载项(1)
现在我想补充一些缺失的信息:首先是Java版本:java版本"1.7.0_45"OpenJDK运行时环境(IcedTea 2.4.3)(suse-8.28.3-x86_64)OpenJDK 64位服务器虚拟机(内部版本24.45-b08,混合模式)
然后有人想看看打开的方法
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)
现在观察到,即使updateOrder()中存在"unpacked=null",也确实会发生NPE。
java运行时可以删除弱引用如果jit优化后没有严格引用,则。源代码似乎一点也不重要。
我用以下方式解决了这个问题:
public void updateOrdering() {
Collections.sort(this.slaves, CMP_IDX_SLV);
}
没有插入任何装饰,以防止从属对象被垃圾收集并且CMP_IDX_SLV中的比较器能够处理对空的弱引用:
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进行排序;将void弱引用放在列表的末尾,以便稍后收集。
我检查了您的源代码,当JIT编译与您的方法"updateOrdering"对应的方法时,我得到了NullPointerException,并且在排序过程中发生了GC。
但当Collections.ort(无论是否打开)=null时,我得到了NullPointerException。这可能是我的示例源代码和您的源代码之间的差异,或者Java版本的差异。我将检查您是否告知Java版本。
我使用的是低于版本的java
java版本"1.7.0_40"Java(TM)SE运行时环境(内部版本1.7.0_40-b43)Java HotSpot(TM)64位服务器虚拟机(内部版本24.0-b56,混合模式)
如果你想在JIT编译中作弊,下面的代码会插入你的源代码,而不是unpacked=null(例如)。然后,JIT编译并不能消除unpacked代码。
long value = unwrapped.size() * unwrapped.size();
if(value * value % 3 == 1) {
//Because value * value % 3 always is 1 or 0, this code can't reach.
//Insert into this the source code that use unwrapped array, for example, show unwrapped array.
}
我的考试成绩如下。
- 如果JIT没有优化与updateOrdering相对应的方法,则不会发生NullPointerException。
- 如果JIT优化了我的方法,那么NullPointerException就会在某个时刻发生。
- 如果JIT优化我的方法插入上面的源代码欺骗JIT编译器,那么不会发生NullPointerException
所以,我(和你)建议JIT优化消除未包装的代码,然后出现NullPointerException。
顺便说一句,如果您想显示JIT编译器优化,可以使用-XX:+PrintCompilation调用java
如果要显示GC,请使用-verbose:GC。
仅供参考,我的示例源代码如下。
public class WeakSampleMain {
private static List<WeakReference<Integer>> weakList = new LinkedList<>();
private static long sum = 0;
public static void main(String[] args) {
System.out.println("start");
int size = 1_000_000;
for(int i = 0; i < size; i++) {
Integer value = Integer.valueOf(i);
weakList.add(new WeakReference<Integer>(value));
}
for(int i = 0; i < 10; i++) {
jitSort();
}
GcTask gcTask = new GcTask();
Thread thread = new Thread(gcTask);
thread.start();
for(int i = 0; i < 100000; i++) {
jitSort();
}
thread.interrupt();
System.out.println(sum);
}
public static void jitSort() {
List<Integer> unwrappedList = unwrapped();
removeNull();
Collections.sort(weakList,
new Comparator<WeakReference<Integer>>() {
@Override
public int compare(WeakReference<Integer> o1,
WeakReference<Integer> o2) {
return Integer.compare(o1.get(), o2.get());
}
}
);
for(int i = 0; i < Math.min(weakList.size(), 1000); i++) {
sum += weakList.get(i).get();
}
unwrappedList = null;
// long value = (sum + unwrappedList.size());
// if((value * value) % 3 == 2) {
// for(int i = 0; i < unwrappedList.size(); i++) {
// System.out.println(unwrappedList.get(i));
// }
// }
}
public static List<Integer> unwrapped() {
ArrayList<Integer> list = new ArrayList<Integer>();
for(WeakReference<Integer> ref : weakList) {
Integer i = ref.get();
if(i != null) {
list.add(i);
}
}
return list;
}
public static void removeNull() {
Iterator<WeakReference<Integer>> itr = weakList.iterator();
while(itr.hasNext()) {
WeakReference<Integer> ref = itr.next();
if(ref.get() == null) {
itr.remove();
}
}
}
public static class GcTask implements Runnable {
private volatile int result = 0;
private List<Integer> stockList = new ArrayList<Integer>();
public void run() {
while(true) {
if(Thread.interrupted()) {
break;
}
int size = 1000000;
stockList = new ArrayList<Integer>(size);
for(int i = 0; i < size; i++) {
stockList.add(new Integer(i));
}
if(System.currentTimeMillis() % 1000 == 0) {
System.out.println("size : " + stockList.size());
}
}
}
public int getResult() {
return result;
}
}
}
对于Java 9,防止JIT丢弃unwrapped
的正确方法是使用Reference.reachabilityFence
:
public void updateOrdering() {
List<T> unwrapped = unwrap();
Collections.sort(this.slaves, CMP_IDX_SLV);
Reference.reachabilityFence(unwrapped);
}
reachabilityFence
调用的存在导致unwrapped
在调用之前被认为是强可达的,从而阻止unwrapped
或其元素的收集,直到sort
完成。(reachabilityFence
的效果似乎以一种奇怪的方式在时间上向后传播,因为它主要表现为JIT指令。)如果没有reachabilityFence
,一旦JIT能够证明它永远不会被访问,即使变量仍在作用域中,也可以收集unwrapped
。
您的问题
如果我删除unpacked=null;这导致在一定负载下运行许多测试时出现CCD_ 31。
根据我的理解,我认为unwrapped = null;
没有任何区别
是的,我也读到过,使objects = null
有时会增加引用对象被GC’ed的概率,但我认为这在这里无关紧要,因为一旦方法结束,unwrapped
的范围就结束了,并且有资格获得GC’ed,并且在您的函数中,Collections.sort(this.slaves, CMP_IDX_SLV);
的排序是在unwrapped = null;
之前完成的,所以在添加或删除它们时获得NPE是没有意义的。
我认为你得到NPE只是一个巧合,我相信如果你再次运行测试,你也会得到NPE。
如果您阅读Java文档
弱引用对象,这不会阻止它们的引用可最终化、最终化,然后回收。弱引用通常用于实现规范化映射
假设垃圾收集器在某个时间点确定某个对象是弱可达的。届时,它将原子性地清除对该对象的所有弱引用,以及对任何其他弱可达对象的所有强引用,从这些对象可以通过强引用和软引用链访问该对象。同时,它将声明所有以前的弱可达对象都是可终结的。同时或在稍后的某个时间,它将对那些在引用队列中注册的新清除的弱引用进行排队。
因此,当您从unwrap()
构造List
时,某些对象可能已标记为finalized
,而当您的null
0正在工作时,某些WeakRefrence
被分配为null
,这确实是可能的。Mattias Buelens提出的观点是完全正确的,你在与编译器的斗争中总是会失败。
如果你同意我的观点,这是JIT中的一个错误吗?
当然不是,我完全不同意你的观点。
我有修改
comparator
的想法,它允许对null
进行弱引用。你觉得怎么样?
我认为这将解决您的一个NPE问题,但您的要求removes void weak references and ensures that weak references are not voided during subsequent sort
不满足
尝试再次调用unwrap
,这将使NPE的窗口几乎为零,
List<T> unwrapped = unwrap();
unwrapped = unwrap(); //Again to eliminate the chances for NPE as now we would have
//already made strong refrences to all objects which have not been `null`