Decoding Java Memory Analyzer Reports: A Step-by-Step Guide for Developers

Memory Analyzers are like surgical knives; you use them when your application experiences serious memory problems such as OutOfMemoryError. You need to have proper knowledge & practice to use this powerful tool to unearth the root cause of memory problems efficiently. To facilitate your learning, this post intends to share key views of Memory Analyzer reports, what information is present in them and how to interpret them easily.

What are the Sections in a Memory Analyzer Report?

Memory Analyzer like HeapHero contains several sections in its heap dump analysis report: 

  1. Leak Suspects
  2. Overview
  3. Dominator Tree
  4. Class Histogram
  5. Threads
  6. Duplicate Classes
  7. GC Roots
  8. Unreachable Objects
  9. OQL
  10. System Properties

Let’s review each of these sections in detail in this post.

1. Leak Suspects

“Leak Suspects” are objects or data structures or threads that hold unusually large amounts of memory or unexpected references. Heap dump analysis tools flag these as potential memory leak sources so you can investigate and fix them before they lead to OutOfMemoryError.

Fig: Memory Leak Suspect Reported By HeapHero

2. Overview

The Overview section in the HeapHero report gives you a quick summary of the heap dump’s metadata and overall memory state. It shows the total heap size, number of class loaders, loaded classes count, objects count, and GC roots count. It also reports the exact time the heap dump was captured. This section helps you to understand the high-level structure and environment of your application at that moment.

Fig: Overview section reporting high-level metrics about the Heap

3. Dominator Tree

The Dominator Tree is the most valuable tool for identifying memory leaks and diagnosing OutOfMemoryErrors. It highlights the largest objects in memory, the amount of memory they retain, and raw application data held within them. Most importantly, it reveals the object reference chain that keeps an object alive. By following this reference chain, you can pinpoint what’s preventing an object from being garbage collected and take corrective action.

Key Information shared in this section are:

a. Largest Object: Dominator Tree shows the largest objects in the memory, size of them & percentage of memory they occupy.

b. Incoming Reference & Outgoing Reference: Using the ‘Incoming References’ & ‘Outgoing References’, you can visualize the object graph of the largest object in the memory. Using the ‘Incoming References’, you can see the reference chain of the objects that keeps the object alive in memory. Using the ‘Outgoing References’, you can see all the child objects. It’s helpful to tell what keeps the object bloated in the memory. 

c. Raw Data: Using the ‘Actual Data’ view in the Dominator Tree section, you can see all the member variables, static variables and their values held by the object.

In essence, the Dominator Tree helps you identify “who is holding on to what” and lets you trace the root cause of memory retention. Here is the Dominator Tree/Largest Objects section from the HeapHero Report

Fig: Dominator Tree showing largest memory consuming Objects

If you would like to learn more about Dominator Tree, you may visit this post: Dominator Tree in Memory Analysis Report.

4. Class Histogram

A Class Histogram is a summary of all the classes that are loaded in the JVM along with the number of objects and the total memory size they occupy. It provides a quick way to understand which object types are consuming memory, which is particularly helpful in identifying memory leaks or excessive memory usage by specific object types.

Class Histogram contains following information reported in table format:

1. Fully Qualified Class Name: Fully qualified name of the class that is present in the memory. Example: java.lang.String

2. Instance Count: Total number of object instances that is present for this class. Example: 10,477

3. Shallow Size: The total amount of memory occupied directly by all instances of this class. It does not include the memory consumed by referenced objects. Example: 2.39MB

4. Retained Size: The total memory that would be freed if all instances of this class and everything reachable only through them were removed. This includes the class’s shallow size and the size of all objects it exclusively retains. Example: 12.46MB

Here is the Historgram section of Heaphero Report:

Fig: Class Histogram showing Instance Count & size of each class

You can also use the Class Histogram to spot memory leaks. Take 2–3 heap dump snapshots at different times and compare their histograms. Objects whose count keeps growing consistently across snapshots often point to a memory leak. Note: Make sure these snapshots are taken during similar load patterns, i.e., during the same load test or under comparable traffic conditions in production. Otherwise, you might misinterpret normal usage growth as a leak.

If you would like to learn more about Class Histogram, you may visit this post: Class Histogram in Memory Analysis Report.

5. Threads

Heap Dump primarily contains object related information , but it also retains basic information about the threads. While the thread information in the heap dump is not very detailed, they are quite helpful. Especially when you’re trying to trace memory leaks tied to thread-local variables or background threads holding on to large objects. 

Here’s what you can expect to see, in the ‘Thread’ section of a Heap Report: 

1. Threads List: This gives you a snapshot of all the threads that were active at the time the heap dump was taken. You can click on any thread to give you more details on its metadata, memory references, and other useful details.

2. Thread Name: Every thread in the JVM has a name (like http-nio-8080-exec-5, Finalizer, or GC Thread). These names often give you clues about which part of your application or framework created the thread, and what its role might be.

3. Thread ID: Each thread is assigned a unique numeric ID by the JVM. This helps you tell threads apart, especially in cases where thread names are reused or not very descriptive.

4. Priority: Threads in Java are assigned priorities ranging from 1 (lowest) to 10 (highest). This number indicates how the JVM might schedule them relative to others.

5. Thread Local Objects: ThreadLocal variables can retain large objects unintentionally, causing memory leaks. This section lists such objects to help you spot leaks tied to threads.

6. Stack Trace (Partial): Although heap dumps don’t contain full thread stack traces, HeapHero tries to reconstruct partial traces.

If any memory leaks origin is pointing to a thread, the Threads section can guide you to the specific thread and its stack trace. This can help you to connect the dots between retained memory and the corresponding line of code causing it.

6. Duplicate Classes

The Duplicate Classes section highlights classes that have been loaded multiple times by different class loaders. This problem surfaces in the applications that use custom class loading mechanisms, modular architectures, or frameworks like OSGi and web containers. Duplicate class loading leads to subtle, hard-to-find bugs and ClassCastExceptions. However, this problem wouldn’t happen in most of the applications.

7. GC Roots

In nature, a tree is alive because of its roots. Similarly, GC Roots are the references which keep the objects alive in memory. Garbage Collectors start scanning from the GC roots to identify the active objects in the memory. The Objects that are not accessible through the chain of references from the GC roots are considered to be eligible for garbage collection. They help you trace why certain objects remain alive and guide you toward uncovering hidden memory leaks. 

Fig: Default view of the GC Roots section

Fig: Drill-down view into a specific GC Root

The first figure shows the default view of the GC Roots section in the HeapHero report. This view lists all the top-level GC Roots currently present in the memory. You can click on any of these entries to explore further. That brings you to the second figure, which shows a detailed view of one of the GC Roots, in this case, the ‘main’ thread.

On the right side of the GC Root table, you’ll see the ‘Actual Data’ panel, which shows the following tabs:

  • Attributes: Lists all member variables of the object referenced by the GC Root.
  • Statics: Shows all static variables associated with the object.
  • Value: Displays the toString() output of the object for a quick glance at its content.
  • Overview: Presents general metadata such as Object ID, Shallow Size, Retained Size, and more.

8. Unreachable Objects

The Unreachable Objects section lists objects that are not referenced by any part of the application and are eligible for garbage collection. These objects are still present in the heap because the GC hadn’t run or completed at the time the heap dump was captured. While unreachable Objects don’t contribute to memory leaks, reviewing them can give an understanding of the allocation patterns or identify temporary objects that could be optimized to reduce memory churn.

9. OQL

OQL (Object Query Language) is a specialized query language that is used to extract and analyze objects in a Java heap dump. Similar to how SQL (Structured Query Language) retrieves data from a database, OQL retrieves information about objects, such as their fields, values, references, sizes, and relationships. It’s useful for diagnosing memory leaks, identifying large object graphs, and exploring how objects are interconnected in the JVM. 

Below are the steps to execute OQL in the heap dump analysis tool HeapHero:

  1. Upload your Heap Dump to the HeapHero.
  2. Once tools parse Heap Dump and generate a memory analysis report, navigate to ‘Object Query Language (OQL)’ section at the bottom of the report.
  3. In this section enter your OQL String that you want to execute. Example: ‘SELECT * FROM java.lang.String’
  4. The tool will execute the query and render the results in a new page. From this point you can execute the new SQL queries on this new page and see the results in the same page. 

Fig: Executing Object Query Language in HeapHero

To learn more about OQL syntax and its advanced capabilities, you may visit this post: What Is OQL and How It Helps in Heap Dump Analysis

10. System Properties

System properties can help answer important environmental related questions:

  • Was the application running on the correct JVM version?
  • Is the heap dump from a 32-bit or 64-bit environment?
  • Is the classpath set correctly?
  • Is the default encoding appropriate for the application?
  • Could any library path misconfiguration lead to memory issues?

This metadata becomes even more valuable when debugging production issues that are hard to reproduce locally. To learn more about System Properties, you may visit this post: Understanding Java System Properties in Memory Analyzer Report.

Fig: System Properties showing environmental settings of the Application

What Are the Key Sections in Memory Analyzer?

Even though Memory Analyzer offers multiple sections, not all of them carry equal weight when troubleshooting memory issues. In most cases, you can find the root cause by focusing on just two key sections:

Even though a memory analyzer contains multiple sections, not all the sections are equally important. Most of the memory problems can be analyzed by reviewing these two sections: 

1. ML-Based Leak Suspect Section: HeapHero uses built-in machine learning to scan your heap dump for common issues—like memory leaks, duplicate objects, inefficient data structures, and more. These issues are surfaced right at the top of your report. Just click any of them to get a closer look: you’ll see which classes or objects are involved, how much memory they’re using, and what’s keeping them alive.

2. Dominator Tree Section: Next go to the Dominator Tree section, which is the most important section in the report. In this section you will see which objects are holding on to the most memory (retained size). Always objects which are holding most of memory are responsible for memory leak. Thus investigate the top nodes in this section. Use the Incoming and Outgoing References to identify who is keeping these objects alive and what objects contain which is causing the memory leak.

Only for complex problems you might need to review other sections like Class Histogram, Duplicate Classes, GC Roots… but most problems can be solved by reviewing the above two sections. 

Conclusion

We hope this post shared the key insights of all the sections of a memory analyzer report. Equipped with this knowledge & few practice sessions, you would be well established to troubleshoot complex memory problems. 

One thought on “Decoding Java Memory Analyzer Reports: A Step-by-Step Guide for Developers

Add yours

Share your Thoughts!

Up ↑

Index

Discover more from HeapHero – Java & Android Heap Dump Analyzer

Subscribe now to keep reading and get access to the full archive.

Continue reading