How to Solve OutOfMemoryError: GC Overhead Limit Exceeded
There are 9 types of java.lang.OutOfMemoryError , each signaling a unique memory-related issue within Java applications. Among these, ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’ stands out as one of the most prevalent and challenging errors developers encounter. In this post, we’ll delve into the root causes behind this error, explore potential solutions, and discuss effective diagnostic methods to troubleshoot this problem. Let’s equip ourselves with the knowledge and tools to conquer this common adversary.
JVM Memory Regions
To better understand OutOfMemoryError, we first need to understand different JVM Memory regions. Here is a video clip that gives a good introduction about different JVM memory regions . But in nutshell, JVM has following memory regions:
What is ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’?
When Java process is spending more than 98% of its time doing garbage collection and recovering less than 2% of the heap and has been doing so far the last 5 (compile time constant) consecutive garbage collections then ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’ gets thrown.
Note: When the above program is executed multiple times, sometimes it may throw ‘java.lang.OutOfMemoryError:Java heap space’ and sometimes it may throw ‘java.lang.OutOfMemoryError:GC overhead limit exceeded’. These two types of OutOfMemoryError can be thrown interchangeably.
What causes ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’?
‘java.lang.OutOfMemoryError: GC overhead limit exceeded’ is triggered by the JVM under following circumstances:
Solutions for ‘OutOfMemoryError: GC overhead limit exceeded’
Following are the potential solutions to fix this error:
Sample Program that generates ‘OutOfMemoryError: GC overhead limit exceeded’
To better understand ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’, let’s try to simulate it. Let’s leverage BuggyApp , a simple open-source chaos engineering project. BuggyApp can generate various sorts of performance problems such as Memory Leak, Thread Leak, Deadlock, multiple BLOCKED threads, … Below is the program from the BuggyApp project that can simulate ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’ when executed.
public class MapManager {
private static HashMap<Object, Object> myMap = new HashMap<>();
public void grow() {
long counter = 0;
while (true) {
if (counter % 1000 == 0) {
System.out.println("Inserted 1000 Records to map!");
}
myMap.put("key" + counter, "Large stringgggggggggggggggggggggggg" + counter);
++counter;
}
}
}
The above program has a ‘MapManager’ class that internally contains a ‘HashMap’ object that is assigned to the ‘myMap’ variable. With in the ‘grow()’ method, there is an infinite ‘while (true)’ loop that keeps populating the ‘HashMap’ object. On every iteration, a new key and value (i.e., ‘key-0’ and ‘Large stringgggggggggggggggggggggggg-0’) is added to the ‘HashMap’. Since it’s an infinite loop, ‘myMap’ object will get continuously populated until heap capacity is saturated. Once the heap capacity limit is exceeded, application will result in ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’.?
How to troubleshoot ‘OutOfMemoryError: GC overhead limit exceeded’?
It’s a two-step process:
1. Capture Heap Dump: You need to capture heap dump from the application, right before JVM throws OutOfMemoryError. In this post, 8 options to capture the heap dump are discussed. You might choose the option that fits your needs. My favorite option is to pass the -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<FILE_PATH_LOCATION> JVM arguments to your application at the time of startup. Example:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/tmp/heapdump.bin
When you pass the above arguments, JVM will generate a heap dump and write it to ‘/opt/tmp/heapdump.bin’ whenever OutOfMemoryError is thrown.
What is Heap Dump? Heap Dump is a snapshot of your application memory. It contains detailed information about the objects and data structures present in the memory. It will tell what objects are present in the memory, whom they are referencing, who are referencing, what is the actual customer data stored in them, what size of they occupy, are they eligible for garbage collection… They provide valuable insights into the memory usage patterns of an application, helping developers identify and resolve memory-related issues.
2. Analyze Heap Dump: Once a heap dump is captured, you need to use tools like HeapHero , JHat, … to analyze the dumps.
How to analyze Heap Dump?
In this section let’s discuss how to analyze heap dump using the HeapHero tool.
HeapHero is available in two modes: 1. Cloud: You can upload the dump to the HeapHero cloud and see the results. 2. On-Prem: You can register here and get the HeapHero installed on your local machine & then do the analysis.? Note: I prefer using the on-prem installation of the tool instead of using the cloud edition, because heap dump tends to contain sensitive information (such as SSN, Credit Card Numbers, VAT, …) and don’t want the dump to be analyzed in external locations.
Once the tool is installed, upload your heap dump to HeapHero tool. Tool will analyze the dump and generate a report. Let’s review the report generated by the tool for the above program.
From the above screenshot you can see HeapHero reporting that a problem has been detected in the application and it points out that the ‘MapManager’ class is occupying 99.96% of overall memory. Tool also provides an overall overview of the memory (Heap Size, Class count, Object Count, …)
Above is the ‘Largest Objects’ section screenshot from the HeapHero’s heap dump analysis report. This section shows the largest objects that are present in the application. In majority of the cases, the top 2 – 3 largest objects in the application are responsible for Memory Leak in the application. Let’s see what are the important information provided in this ‘Largest Objects’ section.
If you notice in #4 and #5, you can see the actual data is present in the memory. Equipped with this information you can know what are the largest objects in the application and what are the values present in the application. If you want to see who created or holding on to the reference of the largest objects, you can use the ‘Incoming References’ feature of the tool. When this option is selected, it will display all the objects that are referencing the ‘MapManager’ class.
From this report you can notice that MapManager is referenced by Object2, which is in turn referenced by Object1, which in turn is referenced by MemoryLeakDemo.
Conclusion
In this post, we’ve covered a range of topics, from understanding JVM memory regions to diagnosing and resolving ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’. We hope you’ve found the information useful and insightful. But our conversation doesn’t end here. Your experiences and insights are invaluable to us and to your fellow readers. We encourage you to share your encounters with ‘java.lang.OutOfMemoryError: GC overhead limit exceeded’ in the comments below. Whether it’s a unique solution you’ve discovered, a best practice you swear by, or even just a personal anecdote, your contributions can enrich the learning experience for everyone.