title: Java "for" statement implementation prevents garbage collecting
body: <p><strong>UPD 21.11.2017:</strong> the bug is fixed in JDK, see <a href="https://stackoverflow.com/a/47404050/907576">comment from Vicente Romero</a></p>
  <p>Summary:</p>
  <p><strong>If <code>for</code> statement is used for any <code>Iterable</code> implementation the collection will remain in the heap memory till the end of current scope (method, statement body) and won't be garbage collected even if you don't have any other references to the collection and the application needs to allocate a new memory.</strong></p>
  <p><a href="http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8175883" rel="noreferrer">http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8175883</a></p>
  <p><a href="https://bugs.openjdk.java.net/browse/JDK-8175883" rel="noreferrer">https://bugs.openjdk.java.net/browse/JDK-8175883</a></p>
  <p><em>The example</em>:</p>
  <p>If i have the next code, which allocates a list of large strings with random content:</p>
  <pre><code>import java.util.ArrayList;
  public class IteratorAndGc {
      
      // number of strings and the size of every string
      static final int N = 7500;
  
      public static void main(String[] args) {
          System.gc();
  
          gcInMethod();
  
          System.gc();
          showMemoryUsage(&quot;GC after the method body&quot;);
  
          ArrayList&lt;String&gt; strings2 = generateLargeStringsArray(N);
          showMemoryUsage(&quot;Third allocation outside the method is always successful&quot;);
      }
  
      // main testable method
      public static void gcInMethod() {
  
          showMemoryUsage(&quot;Before first memory allocating&quot;);
          ArrayList&lt;String&gt; strings = generateLargeStringsArray(N);
          showMemoryUsage(&quot;After first memory allocation&quot;);
  
  
          // this is only one difference - after the iterator created, memory won't be collected till end of this function
          for (String string : strings);
          showMemoryUsage(&quot;After iteration&quot;);
  
          strings = null; // discard the reference to the array
  
          // one says this doesn't guarantee garbage collection,
          // Oracle says &quot;the Java Virtual Machine has made a best effort to reclaim space from all discarded objects&quot;.
          // but no matter - the program behavior remains the same with or without this line. You may skip it and test.
          System.gc();
  
          showMemoryUsage(&quot;After force GC in the method body&quot;);
  
          try {
              System.out.println(&quot;Try to allocate memory in the method body again:&quot;);
              ArrayList&lt;String&gt; strings2 = generateLargeStringsArray(N);
              showMemoryUsage(&quot;After secondary memory allocation&quot;);
          } catch (OutOfMemoryError e) {
              showMemoryUsage(&quot;!!!! Out of memory error !!!!&quot;);
              System.out.println();
          }
      }
      
      // function to allocate and return a reference to a lot of memory
      private static ArrayList&lt;String&gt; generateLargeStringsArray(int N) {
          ArrayList&lt;String&gt; strings = new ArrayList&lt;&gt;(N);
          for (int i = 0; i &lt; N; i++) {
              StringBuilder sb = new StringBuilder(N);
              for (int j = 0; j &lt; N; j++) {
                  sb.append((char)Math.round(Math.random() * 0xFFFF));
              }
              strings.add(sb.toString());
          }
  
          return strings;
      }
  
      // helper method to display current memory status
      public static void showMemoryUsage(String action) {
          long free = Runtime.getRuntime().freeMemory();
          long total = Runtime.getRuntime().totalMemory();
          long max = Runtime.getRuntime().maxMemory();
          long used = total - free;
          System.out.printf(&quot;\t%40s: %10dk of max %10dk%n&quot;, action, used / 1024, max / 1024);
      }
  }
  </code></pre>
  <p>compile and run it with <strong>limited memory</strong>, like this (180mb):</p>
  <pre><code>javac IteratorAndGc.java   &amp;&amp;   java -Xms180m -Xmx180m IteratorAndGc
  </code></pre>
  <p>and at runtime i have:</p>
  <blockquote>
  <p>Before first memory allocating:        1251k of max     176640k</p>
  <p>After first memory allocation:       131426k of max     176640k</p>
  <p>After iteration:                     131426k of max     176640k</p>
  <p>After force GC in the method body:   110682k of max     176640k (almost nothing collected)</p>
  <p>Try to allocate memory in the method body again:</p>
  <pre><code>     !!!! Out of memory error !!!!:     168948k of max     176640k
  </code></pre>
  <p>GC after the method body:               459k of max     176640k (the garbage is collected!)</p>
  <p>Third allocation outside the method is always successful:     117740k of max     163840k</p>
  </blockquote>
  <p>So, inside <em>gcInMethod()</em> i tried to allocate the list, iterate over it, discard the reference to the list, (optional)force garbage collection and allocate similar list again. But i can't allocate second array because of lack of memory.</p>
  <p>In the same time, outside the function body i can successfully force garbage collection (optional) and allocate the same array size again!</p>
  <p>To avoid this <em>OutOfMemoryError</em> inside the function body it's enough to remove/comment only this one line:</p>
  <p><strong><code>for (String string : strings);</code></strong> &lt;-- this is the evil!!!</p>
  <p>and then output looks like this:</p>
  <blockquote>
  <p>Before first memory allocating:        1251k of max     176640k</p>
  <p>After first memory allocation:       131409k of max     176640k</p>
  <p>After iteration:                     131409k of max     176640k</p>
  <p>After force GC in the method body:      497k of max     176640k (the garbage is collected!)</p>
  <p>Try to allocate memory in the method body again:</p>
  <p>After secondary memory allocation:   115541k of max     163840k</p>
  <p>GC after the method body:               493k of max     163840k (the garbage is collected!)</p>
  <p>Third allocation outside the method is always successful:     121300k of max     163840k</p>
  </blockquote>
  <p>So, without <em>for</em> iterating the garbage successfully collected after discarding the reference to the strings, and allocated second time (inside the function body) and allocated third time (outside the method).</p>
  <p>My supposition:</p>
  <p><em>for</em> syntax construction is compiled to</p>
  <pre><code>Iterator iter = strings.iterator();
  while(iter.hasNext()){
      iter.next()
  }
  </code></pre>
  <p>(and i checked this decompiling <code>javap -c IteratorAndGc.class</code>)</p>
  <p>And looks like this <em>iter</em> reference stays in the scope till the end. You don't have access to the reference to nullify it, and GC can't perform the collection.</p>
  <p><strong>Maybe this is normal behavior (maybe even specified in <em>javac</em>, but i haven't found), but IMHO if compiler creates some instances it should care about discarding them from the scope after using.</strong></p>
  <p>That's how i expect to have the implementation of <code>for</code> statement:</p>
  <pre><code>Iterator iter = strings.iterator();
  while(iter.hasNext()){
      iter.next()
  }
  iter = null; // &lt;--- flush the water!
  </code></pre>
  <p>Used java compiler and runtime versions:</p>
  <pre><code>javac 1.8.0_111
  
  java version &quot;1.8.0_111&quot;
  Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
  Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
  </code></pre>
  <p><strong>Note</strong>:</p>
  <ul>
  <li><p>the question is not about programming style, best practices,
  conventions and so on, the question is about an efficiency of Java
  platform.</p>
  </li>
  <li><p>the question is not about <code>System.gc()</code> behavior (you may remove all
  <em>gc</em> calls from the example) - during the second strings allocation the JVM <strong>must</strong> release the dicarded memory.</p>
  </li>
  </ul>
  <p><a href="https://bitbucket.org/snippets/radistao/8eonK#file-IteratorAndGc.java" rel="noreferrer">Reference to the test java class</a>, <a href="https://www.compilejava.net/" rel="noreferrer">Online compiler to test</a> (but this resource has only 50 Mb of heap, so use N = 5000)</p>