java.lang.OutOfMemoryError: GC overhead limit exceeded

Java runtime environment contains a built-in Garbage Collection (GC) process. In many other programming languages, the developers need to manually allocate and free memory regions so that the freed memory can be reused.

Java applications on the other hand only need to allocate memory. Whenever a particular space in memory is no longer used, a separate process called Garbage Collection clears the memory for them. How the GC detects that a particular part of memory is explained in more details in the Garbage Collection Handbook, but you can trust the GC to do its job well.

The java.lang.OutOfMemoryError: GC overhead limit exceeded error means that the GC tried to free memory but was pretty much unable to get anything done. By default it happens when the JVM spends more than 98% of the total time in GC and when after GC less than 2% of the heap is recovered.

java.lang.OutOfMemoryError: GC overhead limit exceeded

The java.lang.OutOfMemoryError: GC overhead limit exceeded error will be displayed when your application has exhausted pretty much all the available memory and GC has repeatedly failed to clean it.

The cause

The java.lang.OutOfMemoryError: GC overhead limit exceeded error is the JVM’s way of signalling that your application spends too much time doing garbage collection with too little result. By default the JVM is configured to throw this error if it spends more than 98% of the total time doing GC and when after the GC less than 2% of the heap is recovered.

What would happen if this GC overhead limit would not exist? Note that the java.lang.OutOfMemoryError: GC overhead limit exceeded error is thrown only when 2% of the memory was freed after several GC cycles. This means that the little amount GC was able to clean will be quickly filled again, forcing GC to restart the cleaning process again. This forms a vicious cycle where the CPU is 100% busy with GC and no actual work can be done. End users of the application face extreme slowdowns – operations which normally complete in milliseconds take minutes to finish.

So the “java.lang.OutOfMemoryError: GC overhead limit exceeded” message is a pretty nice example of a fail fast principle in action.

Examples

In the following example we create a “GC overhead limit exceeded” error by initializing a Map and adding key-value pairs into the map in an unterminated loop:


class Wrapper {
  public static void main(String args[]) throws Exception {
    Map map = System.getProperties();
    Random r = new Random();
    while (true) {
      map.put(r.nextInt(), "value");
    }
  }
}

As you might guess this cannot end well. And, indeed, when we launch the above program with:

java -Xmx100m -XX:+UseParallelGC Wrapper

we soon face the java.lang.OutOfMemoryError: GC overhead limit exceeded message. But the above example is tricky. When launched with different Java heap size or a different GC algorithm, my Mac OS X 10.9.2 with Hotspot 1.7.0_45 will choose to die differently. For example, when I run the program with smaller Java heap size like this:

java -Xmx10m -XX:+UseParallelGC Wrapper

the application will die with a more common java.lang.OutOfMemoryError: Java heap space message that is thrown on Map resize. And when I run it with other garbage collection algorithms besides ParallelGC, such as -XX:+UseConcMarkSweepGC or -XX:+UseG1GC, the error is caught by the default exception handler and is without stacktrace as the heap is exhausted to the extent where the stacktrace cannot even be filled on Exception creation.

These variations are truly good examples that demonstrate that in resource-constrained situations you cannot predict the way your application is going to die so do not base your expectations on a specific sequence of actions to be completed.

The solution

As a tongue-in-cheek solution, if you just wished to get rid of the “java.lang.OutOfMemoryError: GC overhead limit exceeded” message, adding the following to your startup scripts would achieve just that:

-XX:-UseGCOverheadLimit

I would strongly suggest NOT to use this option though – instead of fixing the problem you just postpone the inevitable: the application running out of memory and needing to be fixed. Specifying this option just masks the original java.lang.OutOfMemoryError: GC overhead limit exceeded error with a more familiar message java.lang.OutOfMemoryError: Java heap space.

Another way to give (temporary) relief to GC is to give more memory to the JVM process. Again this is as easy as adding (or increasing if present) just one parameter in your startup scripts:

java -Xmx1024m com.yourcompany.YourClass

In the above example the Java process is given 1GB of heap. Increasing its value will solve your GC overhead limit problem if your application suffered from insufficient memory in the first place.

But if you wish to make sure you have solved the underlying cause instead of masking the symptoms of the “java.lang.OutOfMemoryError: GC overhead limit exceeded” error, you should not stop here. For this you have an arsenal of different tools at your fingertips such as profilers and memory dump analyzers. But be prepared to invest a lot of time and be aware that such tools pose a significant overhead to your Java runtime, thus they are not suitable for production usage.

Our recommendation – try Plumbr for free. When it detects the cause for the java.lang.OutOfMemoryError, it contains and incident alert that contains the exact location of the problem along with solution guidelines.

Can’t figure out what causes your OutOfMemoryError?
Read more.