Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>To keep things in perspective, consider running this code with <code>-Xmx64m</code>:</p> <pre><code>static long sum; public static void main(String[] args) { System.out.println("Warming up..."); for (int i = 0; i &lt; 100_000; i++) test(1); System.out.println("Main call"); test(5_500_000); System.out.println("Sum: " + sum); } static void test(int size) { // for (int i = 0; i &lt; 1; i++) { long[] a2 = new long[size]; sum += a2.length; } long[] a1 = new long[size]; sum += a1.length; } </code></pre> <p>Depending on whether you do the warmup or skip it, it will blow or not blow. This is because the JITted code properly <code>null</code>s out the var, whereas the interpreted code doesn't. Both behaviors are acceptable under the Java Language Specification, which means that you are at the mercy of the JVM with this.</p> <p><sup> Tested with <code>Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)</code> on OS X. </sup></p> <h2>Bytecode analysis</h2> <p>Look at the bytecode with the <code>for</code> loop (simple code, without the <code>sum</code> variable):</p> <pre><code>static void test(int); Code: 0: iconst_0 1: istore_1 2: goto 12 5: iload_0 6: newarray long 8: astore_2 9: iinc 1, 1 12: iload_1 13: iconst_1 14: if_icmplt 5 17: iload_0 18: newarray long 20: astore_1 21: return </code></pre> <p>and without:</p> <pre><code>static void test(int); Code: 0: iload_0 1: newarray long 3: astore_1 4: iload_0 5: newarray long 7: astore_1 8: return </code></pre> <p>No explicit <code>null</code>ing out in either case, but note that in the <em>no-for</em> example the same memory location is actually reused, in contrast with the <em>for</em> example. This would, if anything, lead to the expectation opposite to the observed behavior.</p> <h2>A twist...</h2> <p>Based on what we learned from the bytecode, try running this:</p> <pre><code>public static void main(String[] args) { { long[] a1 = new long[5_000_000]; } long[] a2 = new long[0]; long[] a3 = new long[5_000_000]; } </code></pre> <p><strong>No OOME thrown</strong>. Comment out the declaration of <code>a2</code>, and it is back. We <strong>allocate more</strong>, but <strong>occupy less</strong>? Look at the bytecode:</p> <pre><code>public static void main(java.lang.String[]); Code: 0: ldc #16 // int 5000000 2: istore_1 3: ldc #16 // int 5000000 5: newarray long 7: astore_2 8: iconst_0 9: newarray long 11: astore_2 12: ldc #16 // int 5000000 14: newarray long 16: astore_3 17: return </code></pre> <p>The location 2, used for <code>a1</code>, is reused for <code>a2</code>. The same is true for OP's code, but now we overwrite the location with a reference to an innocuous zero-length array, and use another location to store the reference to our huge array.</p> <h2>To sum it up...</h2> <p>Java Language Specification does not specify that any garbage object <em>must</em> be collected and the JVM spec only says that the "frame" with local variables is destroyed as a whole upon method completion. Therefore all behaviors that we have witnessed are by the book. The <em>invisible</em> state of an object (mentioned in the document linked to by <em>keppil</em>) is just a way to describe what happens to go on in some implementations and under some circumstances, but is in no way any kind of canonical behavior.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload