Operations 12 min read

Analyzing Java OutOfMemoryError with Eclipse MAT: A Step‑by‑Step Guide

This article demonstrates how to use Eclipse Memory Analyzer (MAT) together with heap dump files to diagnose Java OutOfMemoryError issues, covering heap dump generation, histogram and dominator tree analysis, reference chain inspection, OQL queries, and a concrete Spring Boot example.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Analyzing Java OutOfMemoryError with Eclipse MAT: A Step‑by‑Step Guide

Hello everyone, I'm Chen. After many reader requests, I’m announcing that the "Spring Cloud Alibaba Microservice Project Practice" tutorial will be released soon as articles, videos, and source code.

Now we focus on how to use the JVM heap dump tool MAT to analyze OOM problems.

Using MAT to Analyze OOM Issues

Analyzing heap dumps is the most effective way to investigate OOM problems because the dump contains a full snapshot of the heap and thread stacks (available since Java 6 Update 14).

Typical JVM options for automatic heap dump on OOM are:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=.

I recommend Eclipse Memory Analyzer (MAT). Download it from https://www.eclipse.org/mat/ .

The analysis workflow usually follows these steps:

Use the Dominator Tree or Histogram to find the memory‑heavy types and get a rough cause of the leak.

Inspect the detailed object list and reference chains of the heavy types to pinpoint the exact leak location.

Examine object attributes to understand values and dependencies without needing source code.

Check thread stacks to see if excessive threads contribute to OOM and locate the thread that threw the exception.

In the example, we open a dump file java_pid29569.hprof with MAT. The overview shows a total heap of 437.6 MB.

The histogram reveals that char[] arrays and String objects dominate memory usage, suggesting that large strings are filling the heap.

By right‑clicking a char[] entry and selecting “List objects → with incoming references”, we can view all instances and their reference chains.

Expanding a char[] shows that each array holds about 10 000 characters (≈20 KB) and is referenced by a String , which is stored in an ArrayList inside FooService . The ArrayList itself retains ~431 MB.

char[] → 10 000 chars → ~20 KB.

char[] is referenced by String.value .

String is held in ArrayList.elementData .

ArrayList is referenced by FooService.data , whose retained heap is 431 MB.

“Retained Heap” includes the object and everything it references; “Shallow Heap” is just the object itself. Thus, although FooService.data is only 16 bytes, its associated objects consume hundreds of megabytes.

We can also view the full string content by copying the value from the object inspector.

The Dominator Tree (third toolbar button) lists objects by retained heap size, confirming FooService as the top offender:

Path: FooService → ArrayList → Object[] → String → char[] with 21 523 string instances.

Inspecting the thread view (fifth toolbar button) shows the main thread calling FooService.oom() , which is invoked by OOMApplication.run() and ultimately by Spring Boot’s startup runner.

The call stack reveals that a large StringBuilder.append triggers the OOM, and the FooService is a Spring singleton bean, repeatedly adding 10 KB strings to the same list.

Using the OQL view (fourth toolbar button), we can query the dump with SQL‑like syntax, e.g.:

SELECT * FROM org.geekbang.time.commonmistakes.troubleshootingtools.oom.FooService

The query returns a single FooService instance. Further inspection shows two references: one from OOMApplication and another from Spring’s singletonObjects map, confirming it is a singleton bean.

Finally, the original source code matches the analysis:

@SpringBootApplication
public class OOMApplication implements CommandLineRunner {
    @Autowired
    FooService fooService;
    public static void main(String[] args) {
        SpringApplication.run(OOMApplication.class, args);
    }
    @Override
    public void run(String... args) throws Exception {
        // Continuously call FooService.oom()
        while (true) {
            fooService.oom();
        }
    }
}

@Component
public class FooService {
    List
data = new ArrayList<>();
    public void oom() {
        // Add a 10KB string to the same list repeatedly
        data.add(IntStream.rangeClosed(1, 10_000)
                .mapToObj(__ -> "a")
                .collect(Collectors.joining("")));
    }
}

Through MAT’s object list, dominant objects, and thread stack views, we traced the OOM to a continuously growing list in a Spring Boot service, demonstrating how a heap dump provides a near‑source‑code snapshot for deep troubleshooting.

Final Note (Please Support)

The author has compiled three columns into PDFs. To obtain them, follow the links, subscribe to the "Code Monkey Technical Column" public account, and reply with the respective keywords.

If this article helped you, please like, view, share, and bookmark—it’s the biggest motivation for the author to keep producing content.

Javaperformancememory analysisoomHeap DumpEclipse MAT
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.