java.lang.OutOfMemoryError: GC overhead limit exceeded

Java applications include a built-in Garbage Collection (GC). 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 you. How the GC detects that a particular part of memory is no longer used is out of scope for this article, but you can trust the GC in this regard.

The “java.lang.OutOfMemoryError: GC overhead limit exceeded” error means that GC has been trying to free the memory but is pretty much unable to get any job done. By default it happens when the JVM is spending more than 98% of the total time in GC. In addition, after GC less than 2% of the heap is recovered.

java.lang.OutOfMemoryError: GC overhead limit exceeded

So – 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.

Cause of java.lang.OutOfMemoryError

When failing with the “java.lang.OutOfMemoryError: GC overhead limit exceeded” error, the JVM is signalling that your application is spending too much time in garbage collection with little to show for it. By default the JVM is configured to throw this error if you are spending more than 98% of the total time in GC and after the GC less than 2% of the heap is recovered.

What would happen if this GC overhead limit was not present? Note that the “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 thus 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 are facing extreme slowdowns – operations which used to be completed in milliseconds are now likely to 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.

Example of java.lang.OutOfMemoryError

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 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 launched with:

java -Xmx100m -XX:+UseParallelGC Wrapper

we are facing our “java.lang.OutOfMemoryError: GC overhead limit exceeded” message. But this example is rather 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 running with smaller Java heap size such as the following:

java -Xmx10m -XX:+UseParallelGC Wrapper

the application will die with a more familiar “java.lang.OutOfMemoryError: Java heap space” message being thrown on Map resize. And when running with other garbage collection algorithms besides ParallelGC, such as -XX:+UseConcMarkSweepGC or -XX:+UseG1GC, the error you face will be 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 a truly clear example that on 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.

Solution for java.lang.OutOfMemoryError

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 do just that:

-XX:-UseGCOverheadLimit

I would strongly suggest NOT to use this option though – instead of fixing the problem you are just postponing the inevitable: the application is running out of memory and needs 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 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 – go ahead and try out Plumbr for free. When we find the cause for the java.lang.OutOfMemoryError you will see the exact location of the problem along with solution guidelines.

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