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.