JIT优化和弱引用



我有以下代码:

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。

由于unwrappedslaves中的每个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,而当您的null0正在工作时,某些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`

相关内容

  • 没有找到相关文章

最新更新