Production Performance Problems are tricky to solve, Memory Leaks are especially the trickiest ones to solve among them. In this post let’s discuss the most commonly observed memory leak that we face in our Java applications and how to diagnose and resolve them quickly.
Video
In this video, our Architect Ram Lakshmanan has explained about the most commonly observed memory leak that we face in our Java applications and how to diagnose and resolve them quickly.
Most Common Memory Leak – Forever Growing Object
Let’s start our discussion with the most common form of memory leak that we face in our application. In our java application we declare Java Collection objects such as HashMap, ArrayList, TreeSet …. as static and member variables. To the Collection Object, we add records. Sometimes due to the bug in the program or spikes in the traffic volume, we keep adding records continuously while removing the records from them. Thus, the Collection Object ‘s size will start to grow over the period of time. Once its size grows beyond the allocated heap size (i.e., -Xmx size), JVM will throw OutOfMemoryError. Some common examples are Caching without expiry, storing user actions/error logs for analytics, session management in web applications, …
Let’s review the below sample program which simulate the memory leak that we described just now:
1: public class MapManager {
2:
3: private HashMap<Object, Object> myMap = new HashMap<>();
4:
5: public void grow() {
6:
7: long counter = 0;
8: while (true) {
9:
10: myMap.put("key" + counter, "Large stringgggggggggggggggggggggggggggg"
11: + "gggggggggggggggggggggggggggggggggggggggg" + counter);
12 }
13: }
14: }
Here in line #3, a ‘HashMap’ object is created. In line #10, records are added to this ‘HashMap’ object. In line #8, the ‘while’ condition is specified as ‘true’. (Note: Developers typically do not explicitly use ‘true’ as the condition; in real-world cases, such behavior occurs due to a logical error or missing termination condition.) Because of this, the thread will continuously add records to the ‘HashMap’, causing the memory leak. When the size of the ‘HashMap’ exceeds the JVM’s heap memory limit, the JVM will throw a ‘java.lang.OutOfMemoryError’.
Not So Common Memory Leaks
When we say common memory leaks, it also implies that there are not so common memory leaks as well. Such less common memory leaks scenarios are:
a. Thread Leaks
d. Metadata objects growing in Metaspace
e. Collection Objects with Unused References
f. Listeners and Callbacks Not Deregistered
Our experts walk through these memory leaks in the webinar—watch the video here.
Symptoms of Memory Leak
When there is a memory leak in your application, you will observe one or combination of the below mentioned symptoms:
1. Gradual Memory Increase: You will see a gradual increase in the application’s memory consumption over a time period.
2. CPU Spike: As memory leak worsen, CPU consumption will spike up to 100% and remain there.
3. Degraded Response Time/Timeouts: As memory leaks worsen, application’s response time degrades and timeout will start to happen, which ultimately causes the application’s health checks to fail.
4. OutOfMemoryError: Finally, JVM will throw ‘java.lang.OutOfMemoryError: Java heap space’ to rescue the application from Memory Leak.
You may visit this post, to learn more about the details of memory leak symptoms.
Troubleshooting Memory Leak
Troubleshooting memory leak involves 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 they are referencing, what is the actual customer data stored in them, what size 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.
Analyzing 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.

Fig: HeapHero reporting Memory problem detected
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, …)

Fig: Largest objects in the application
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.

Fig: Largest Objects Breakdown
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.

Fig: Incoming references of the Selected Object
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.
Predict Memory Leaks in Performance Labs
There is an age-old saying: ‘Prevention is better than Cure’. Similarly, if we can prevent Memory Leaks from happening rather than fire fighting in the production environment will be a much better experience. Here are 2 micro-metrics, which can predict memory leaks accurately:
1. Garbage Collection Behavior: First symptom of Memory Leak is i.e. ‘Gradual Memory Increases Over Time’. Thus in performance labs, if we can study the Garbage Collection behaviour, it will clearly point out whether memory consumption is increasing or not.
2. Object Creation Rate: Object Creation Rate reports the amount of objects created in a unit of time. Example: 100 mb/sec. Say in your previous release, your application’s object creation rate was 50 mb/sec , whereas in the new release it has become 100 mb/sec, then it indicates that your application is creating 2x more objects to service the same workload. It clearly shows that some inefficiency has been committed by the developer in the new release, which has the potential to cause memory problems in the application.
On how to source these metrics and study them you can refer to this 9 micro-metrics which can forecast production problems in the performance labs post
Conclusion
In this post we discussed the most common memory leak pattern, its symptoms and effective diagnostic approach and preventative mechanisms. Hopefully you found it helpful.

Hello everyone,
I found this blog very insightful, especially the breakdown of common memory leaks like growing collections and the heap dump strategy. I’d love to know: What practical early signs should I monitor in my application to detect memory leaks before they lead to performance degradation or OutOfMemoryError?
For example, should I look at GC patterns or object creation rates? What tools or simple metrics do you recommend for catching these issues early during development?
Thanks for your guidance!
Great question, Annya!
Early warning signs include:
1. Rising heap usage that doesn’t reset after full GC.
2. Frequent/full GCs with little memory reclaimed.
3. Slower response times under steady load.
Tools like HeapHero, Eclipse MAT, or JVisualVM can spot these issues early. Monitoring GC logs and object creation rates during development is a practical way to catch leaks before they escalate.
How can CPU spikes help detect a memory leak even before an OutOfMemoryError occurs?
Nice one, Santhosh!
CPU spikes often occur because GC runs more frequently as memory fills up. If CPU stays high while heap usage doesn’t drop much, it’s a strong signal of a memory leak. Watching GC activity alongside CPU can reveal leaks even before an OOM error happens.
What approach can we take to compare object creation rates across releases in order to spot potential memory regressions?
Great point!
You can track object allocation rates in performance tests across versions. A sudden jump without a clear functional reason may indicate a leak or regression. Tools like HeapHero or GC log analyzers help automate this comparison and highlight trends.
When monitoring JVM metrics, which is a stronger indicator of a memory leak, steadily rising heap usage, or high GC frequency with little memory reclaimed? Why?
Good question, Jessy!
Both are useful, but rising heap usage after full GC is usually the strongest indicator of a leak, because it shows memory isn’t being released. High GC frequency with little memory reclaimed often comes later, when the leak worsens. Together, they confirm the diagnosis.
This is a great blog! It was knowledgable and covered so many aspects of memory leaks in java! In the MapManager example that you guys used, you mentioned the leak which is caused by the growing HashMap. When it comes to real world systems, how do you think developers can identify if it is a memory leak or if it’s usual cache growth? Does HeapHero have any tools/ analyses to highlight this difference?
Excellent question, Snigdha!
The difference between normal cache growth and a leak is intent and management:
1. Caches usually have eviction policies (e.g., LRU).
2. Leaks keep references indefinitely.
HeapHero and similar tools can show reference chains to highlight whether objects are being retained by design (cache) or unintentionally (leak).
When working with Android application, this is a very common issue. The end result is ANR (Application Not Responding). This includes application leak when deal with bitmaps and activities (static reference).
In Android apps, memory leaks with bitmaps or static references to activities are indeed a major cause of ANRs. Some best practices are:
Use WeakReference or clear references when activities are destroyed.
For bitmaps, prefer inBitmap reuse and recycle unused images promptly.
Monitor leaks with tools like LeakCanary or Android Studio Profiler to catch them early.
This way, you can avoid memory buildup that leads to Application Not Responding errors.