4

I recently tried to activate the garbage-first garbage collector and evaluate it. As a start I wrote this code, trying to produce an java.lang.OutOfMemoryError:

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class G1Test {
  public static void floodMemory() {
    int i = 0;
    try {
      // allocate an array where we will just store a lot of java objects
      List<Date> l = new ArrayList<Date>();
      for (; i < 1051366050; i++) {
        l.add(new Date());
      }
    } catch (Throwable t) {
      System.err.println("Throwable in floodMemory!");
      System.out.println("i=" + i);
      t.printStackTrace();
    }
  }

  public static void main(String[] args) {
    try {
      System.out.println("Started memory flooding.");
      floodMemory();
      System.out.println("Sleeping.");
      Thread.sleep(Long.MAX_VALUE);
    } catch (Throwable t) {
      System.err.println("Throwable in main!");
      t.printStackTrace();
    }
  }
}

... and I ran the code using two scenarios:

Case 1. With these flags: -Xmx4096M -XX:+UseG1GC, I get this output:

    Started memory flooding.
    Throwable in main!
    java.lang.OutOfMemoryError: Java heap space
    at com.siemens.scr.usi.experimental.G1Test.floodMemory(G1Test.java:14)
    at com.siemens.scr.usi.experimental.G1Test.main(G1Test.java:26)

... which means that an the infamous OutOfMemoryError is thrown somewhere BUT captured in the main method.

Case 2. With this flag: -Xmx4096M, I get this output:

Started memory flooding.
Throwable in floodMemory!
i=105136605
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2245)
    at java.util.Arrays.copyOf(Arrays.java:2219)
    at java.util.ArrayList.grow(ArrayList.java:242)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
    at java.util.ArrayList.add(ArrayList.java:440)
    at com.siemens.scr.usi.experimental.G1Test.floodMemory(G1Test.java:14)
    at com.siemens.scr.usi.experimental.G1Test.main(G1Test.java:26)
Sleeping.

... which means that the exception is caught where I was expecting it to be caught.

Notes:

  1. The code is pure experimental and doesn't follow any specific purpose - it is just to observe the behavior.
  2. The code ran using Oracle JDK 1.7.0 update 60, 64 bit on Windows 7 Enterprise running on top of a Dell Precision M4700.

The question is if somebody can explain this behavior - couldn't find any similar posts nor any bug report (my issue being the lack of consistency).

cristi
  • 161
  • 1
  • 2
  • 7
  • 2
    can you consistently repeat the same behavior for the same collector ? – jmj Jul 16 '14 at 00:06
  • If you did not catch the exception in main, would it be caught and reported as expected. – Scary Wombat Jul 16 '14 at 00:42
  • 1
    Note that the exception could have occurred on the `new Date()` operation, vs in `add`, accounting for the stack trace. And I don't know what "garbage first" is supposed to do, but it's possible that the exception could not be handled by the inner `catch` because there was not enough memory. – Hot Licks Jul 16 '14 at 00:51
  • @JigarJoshi - yes, the behavior can be repeated. The reason I placed here all the code is because I hope somebody else can run it and confirm. – cristi Jul 16 '14 at 12:41
  • @ScaryWombat - the exception is caught in two different places. My expectation is that `floodMemory` method would let nothing out of it, including the OEM. – cristi Jul 16 '14 at 12:44
  • @HotLicks - that might be very well correct. The fact that the heap is so exhausted that even catching an exception would fail. When the `floodMemory` ends then the entire list can be released so the JVM will find place to "do something". However, I would expect consistency. – cristi Jul 16 '14 at 12:44

1 Answers1

0

This is just a theory, but it may not have anything to do with the garbage collection settings, at least not directly:

In the first case (-Xmx4096M -XX:+UseG1GC): the OutOfMemoryError could've been thrown in the method floodMemory, as expected, but since you're all ready at an OutOfMemory state, it may be possible that another OutOfMemoryError was thrown within the catch block of the floodMemory method. It may be possible that the second one is being thrown at the System.err command, which is why you don't see the output of the first. This error then gets propagated to the main method.

In the second case, the Garbage Collector might've been able to free enough memory for the System.err to execute and the rest of your application to finish.

Again, this is just a theory. I tried running the first case with Java 1.7 and eventually my program just hung and I didn't see an exception thrown.

lordoku
  • 908
  • 8
  • 22
  • Traditionally, a heap algorithm "holds back" some amount of heap that will only be made available when an OOM occurs. (Similarly for stack overflow.) But it's pretty tricky picking the "right" amount of storage to "hold back", and the amount would very likely change if the heap management algorithm were changed. One suspects that the G1GC implementation has not "tuned" this "knob" appropriately. – Hot Licks Jul 16 '14 at 20:40
  • That's a good point. What I wanted to get out of my answer was the fact that the second exception was most likely coming out inside of the catch block. – lordoku Jul 17 '14 at 14:30