Java – JIT optimization and weak references
I have the following code:
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,.... }
The unwrap () method simply creates a t list referenced by weak references in slaves, and eliminates null weak references in dependencies as a side effect Then there is the sort that depends on each slave member referencing some T; Otherwise, the code will generate NullPointerException
Since unwrapped has a reference on each t in the slave, there is no GC to eliminate T. finally, unwrapped = null eliminates the reference on unwrapped, and then publishes GC again It seems to work well
Now my question:
If I delete unwrapped = null; This results in nullpointerexceptions when running many tests under some load I suspect that JIT eliminates list < T > unwrapped = unwrap(); Therefore, GC is applicable to t
Do you have any other explanation? If you agree with me, is this an error in JIT?
Personally, I don't think unwrapped = null is necessary, because once updateordering () returns, unwrapped will be deleted from the frame Are there specifications that can be optimized and not optimized?
Or did I do things the wrong way? I have an idea to modify the comparator, which allows weak references to null How did you like it?
Thank you for your advice
Join (1)
Now I want to add some missing information: first, the Java version: Java version "1.7.0_45" openjdk runtime environment (icedtea 2.4.3) (suse-8.28.3-x86_64) openjdk 64 bit server VM (built-in 24.45-b08, mixed mode)
Then someone wants to see how it unfolds
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; }
Note that the void reference will be removed during the iteration In fact, I replaced it with this method
private synchronized List<T> unwrap() { List<T> res = new ArrayList<T>(); for (T cand : this) { assert cand != null; res.add(cand); } return res; }
I use my own iterator, but it should be functionally the same
Then someone lingers in the stack trace This is a piece
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)
It points to the comparator and returns the line
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
last,
private final static IdxComparator CMP_IDX_SLV = new IdxComparator();
Is an important constant
Join (2)
Now observe that NPE occurs even if 'unwrapped = null' exists in updateordering()
The Java runtime may delete weak references if there are no strict references after JIT optimization The source code doesn't seem to matter
I solved the problem in the following ways:
public void updateOrdering() { Collections.sort(this.slaves,CMP_IDX_SLV); }
No decorations were inserted to prevent slaves from being garbage collected and enable CMP_ IDX_ The comparator in SLV handles weak references to 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(); }
As a side effect, order the basic list > slave; Place the void weak reference at the end of the list where you can collect it later
Solution
I check your source code. When JIT compiles my method corresponding to your method "updateordering" and GC occurs during sorting, I get NullPointerException
But when collections Sort whether unwrapped = null or not, I get NullPointerException This may occur between my sample source code and your sample source code, or Java version differences I'll check if you tell the Java version
I use the following version of 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)
If you want to cheat JIT compilation, insert the following code into your source code instead of unwrapped = null (for example) Then, JIT compilation does not eliminate unwrapped code
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. }
My test scores are as follows
>If the JIT does not optimize my method corresponding to updateordering, NullPointerException. > If JIT optimizes my method, NullPointerException will occur at some time
>If the JIT optimizer inserts the above code into my method to deceive the JIT compiler, NullPointerException will not occur
Therefore, I (and you) suggest that JIT Optimze eliminate the unpacked code, and then NullPointerException occurs
By the way, if you want to display JIT compiler optimizations, you can use - XX: printcompilation to call Java If you want to display GC, use - verbose: GC
For reference only, my sample source code is as follows
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; } } }