JIT优化和弱引用

时间:2013-12-28 12:35:16

标签: java reference garbage-collection weak

我有以下代码:

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()只创建T中由弱引用引用的slaves列表 并且作为副作用消除了null中引用slaves的弱引用。 然后是依赖于slaves的每个成员引用一些T的排序; 否则代码会产生NullPointerException

由于unwrapped拥有T中每个slaves的引用,因此在排序过程中没有GC会删除T。最后,unwrapped = null取消了对展开的引用 然后再次发布GC。似乎工作得很好。

现在我的问题:

如果我删除unwrapped = null;,则在某些负载下运行多个测试时会导致NullPointerExceptions。我怀疑JIT消除List<T> unwrapped = unwrap(); 所以GC在排序过程中适用于奴隶中的T

你还有其他解释吗?如果您同意我的意见,这是JIT中的错误吗?

我个人认为unwrapped = null不一定是必要的,因为只要unwrapped返回updateOrdering()就会从框架中移除null。是否存在可以优化和不优化的规范?

或者我是以错误的方式做事了?我有想法修改比较器,它允许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; } 上的弱引用。你觉得怎么样?

感谢您的建议。

加上(1)

现在我想添加一些缺失的信息: 首先是Java版本: java版“1.7.0_45” OpenJDK运行时环境(IcedTea 2.4.3)(suse-8.28.3-x86_64) OpenJDK 64位服务器VM(构建24.45-b08,混合模式)

然后有人想看方法展开

private synchronized List<T> unwrap() {
List<T> res = new ArrayList<T>();
for (T cand : this) {
    assert cand != null;
    res.add(cand);
}

return res;
}

请注意,在迭代时,将删除void引用。 实际上我用

替换了这个方法
 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();

最后,

public void updateOrdering() {
Collections.sort(this.slaves, CMP_IDX_SLV);
}

是一个重要的常数。

加上(2)

现在观察到即使在updateOrdering()中存在'unwrapped = null',也会发生NPE。

java运行时可能会删除弱引用 如果在jit优化之后没有严格的引用 。 源代码似乎并不重要。

我通过以下方式解决了问题:

    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();
    }

未插入任何装饰以防止奴隶被垃圾收集 并启用CMP_IDX_SLV中的比较器来处理对null的弱引用:

{{1}}

作为副作用,订购基础列表List&gt;奴隶; 将void弱引用放在列表的末尾,以后可以在其中收集。

3 个答案:

答案 0 :(得分:2)

我检查了你的源代码,当JIT编译我的方法对应你的方法“updateOrdering”并且GC在排序过程中发生时,我得到了NullPointerException。

但是当Collections.sort无论是否有unwrapped = null时,我都得到了NullPointerException。 这可能发生在我的示例源代码和您的示例源代码之间的差异,或Java版本差异。我会检查你是否告诉Java版本。

我使用java以下版本。

java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)

如果你想欺骗JIT编译,下面的代码插入你的源代码而不是unwrapped = null(例如)。然后,JIT编译不会消除未包装的代码。

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 optimze删除未解包的代码,然后发生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;
        }
    }
}

答案 1 :(得分:0)

你的问题

  

如果我删除unwrapped = null;在某些负载下运行多个测试时,这会导致NullPointerException

根据我的理解,我认为unwrapped = null;没有任何区别 是的,我还读过,有时候使objects = null增加引用的对象将被GC的概率,但我认为这不重要,因为一旦方法结束,unwrapped的范围结束并且是符合GC的条件,并且在Collections.sort(this.slaves, CMP_IDX_SLV);功能排序unwrapped = null;之前完成,因此在添加或删除NPE时无法获得NPE。

我认为你获得NPE只是巧合,我相信如果再次进行测试,你也会得到NPE。

如果您阅读Java Documentation

  

弱引用对象,它们不会阻止它们的指示物被最终化,最终化,然后回收。弱引用最常用于实现规范化映射   假设垃圾收集器在某个时间点确定对象是弱可达的。那时它将原子地清除对该对象的所有弱引用以及对任何其他弱可达对象的所有弱引用,通过一系列强引用和软引用可以从该对象到达该对象。同时,它将声明所有以前弱可达的对象可以最终确定。在同一时间或稍后,它会将那些新引用的弱引用排入队列。

因此,当您从List构建unwrap()时,某些对象可能已标记为finalized并且Collection.sort正在使用某些WeakRefrence被分配nullMattias 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`

答案 2 :(得分:0)

从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就可以被收集。证明即使变量仍在范围内,也将永远不会再次访问它。