Java 10: Byte Code Generation for Enhanced For Loops

user9584853 :

The following example describes the generation of the following lines of code until Java 9.

List data = new ArrayList<>();for (String b : data); 

public class Test

{
  public Test() {}
  public static void main(String[] paramArrayOfString) throws IOException {
    ArrayList localArrayList = new ArrayList();
    String str;
    for (Iterator localIterator = localArrayList.iterator(); localIterator.hasNext(); str = (String)localIterator.next()) {}
}

In Java 10, iterator variables are declared outside for loops and initialized to the null value immediately once the operation is over, so GC can get rid of unused memory.

{
    Iterator iterator = data.iterator();
    for (; iterator.hasNext();) 
    {
        String b = (String)iterator.next();
    }
    b = null;
    iterator = null;
}

How is setting reference null explicitly better than reference going out of scope by the end of the for loop.

Source: https://dzone.com/articles/features-in-java-10

Also, adding link from the comments : https://bugs.openjdk.java.net/browse/JDK-8192858

d.j.brown :

Edit: There already exists a related question: Java "for" statement implementation prevents garbage collecting that provides more information.

After reading through the bug-reports (https://bugs.openjdk.java.net/browse/JDK-8192858 and https://bugs.openjdk.java.net/browse/JDK-8175883) the reason for this change can be summarised as the following:

There was an issue in the bytecode produced by javac which resulted in a reference to an array / iterable being kept after the completion of the loop.

As a result, even if an array / iterable is explicitly nullified, there is still a reference to the array / iterable which means that the array / iterable was not eligible for garbage collection until leaving the scope of the method.

With large arrays / iterables (as per the example below) this could result in an OutOfMemoryError.

This is demonstrated by this use case here (taken from the bug report):

public class IteratorInOneScope { 

    private static final int HALF_OF_MEMORY = (int) (Runtime.getRuntime().maxMemory() * 0.5); 

    public static void main(String[] args) { 
        byte[] data = new byte[HALF_OF_MEMORY]; 

        for (byte b : data); // <-- if you comment this line - the application finished successfully 
        data = null; // this expects to discard reference -> allow to release the memory 

        byte[] data2 = new byte[HALF_OF_MEMORY]; // the memory can't be allocated second time, if the "for" loop statement above is used 

        System.out.println("Success"); 
    } 
}

Which compiled to the following bytecode:

 0: getstatic #2 // Field HALF_OF_MEMORY:I 
 3: newarray byte 
 5: astore_0 <==== array ref in slot #0 

 6: aload_0 
 7: astore_1 <==== array ref in slot #1 

 8: aload_1 
 9: arraylength 
10: istore_2 

11: iconst_0 
12: istore_3 

13: iload_3 
14: iload_2 
15: if_icmpge 29 

18: aload_1 
19: iload_3 
20: baload 
21: istore 4 
23: iinc 3, 1 
26: goto 13 

29: aconst_null 
30: astore_0 <== nulls slot #0 

31: getstatic #2 // Field HALF_OF_MEMORY:I 
34: newarray byte 
36: astore_1 
37: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 
40: ldc #4 // String Success 
42: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
45: return 

There is no update to JLS 14.14.2, for this reason:

JLS 14.14.2 is only concerned with semantics of the statement, not garbage collection behavior. Compilers are free to generate whatever bytecode they want that produces the specified behavior. So, if javac wants to set some unused locals to null, it's free to do so. No spec change necessary, and it would be a mistake to include it in the spec, because it doesn't impact the statement's semantics.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=442178&siteId=1