Simulating and Diagnosing JVM Out-of-Memory Errors in Spring Boot

Reproducing Heap Exhaustion

Bootstrap a Spring Boot application and add the web starter dependancy:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Implement an endpoint that continuously reserves large blocks of memory:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/stress")
public class HeapExhaustionEndpoint {

    private final List<byte[]> payloadBuffer = new ArrayList<>();

    @GetMapping("/consume")
    public String exhaustHeap() {
        int chunk = 2 * 1024 * 1024; // 2 MB per allocation
        while (true) {
            payloadBuffer.add(new byte[chunk]);
        }
    }
}

Package the application:

mvn clean package -DskipTests

Move the resulting JAR to the target server.

Running with Constrained Heap

Limit the heap to 48 MB so the error surfaces quickly:

nohup java -Xms48m -Xmx48m -jar target/app.jar > application.log 2>&1 &

Flag details:

  • -Xms48m -Xmx48m locks initial and maximum heap size to 48 MB.
  • nohup ... & runs the process in the background and shields it from hangup signals.
  • > application.log 2>&1 redirects stderr into stdout and writes both to the same log file.

Inducing the Failure

Call the endpoint from the host or any reachable client:

curl http://localhost:8080/stress/consume

Once the heap saturates, the framework returns an HTTP 500 response:

{"timestamp":"2024-07-15T06:47:38.083+00:00","status":500,"error":"Internal Server Error","path":"/stress/consume"}

Log Examination

Inspect the log for the underlying cause:

tail -n 100 application.log

You will find java.lang.OutOfMemoryError: Java heap space together with the associated stack trace.

Acquiring Heap and Thread Dumps

Identify the JVM process ID:

jps -lv

Or alternatively:

ps aux | grep java

Generate a binary heap dump:

jmap -dump:live,format=b,file=heap.hprof <pid>

Record the thread state:

jstack -l <pid> > threads.txt

Heap Dump Inspection with MAT

Load heap.hprof into Eclipse Memory Analyzer. When the import dialog appears, choose Leak Suspects Report. MAT automatically identifies the object graph path that retains the most memory.

In this case, the report highlights an ArrayList instance held by HeapExhaustionEndpoint that keeps a growing set of byte[] objects. Clicking Details on the dominant suspect reveals the retained heap breakdown, showing the 2 MB arrays consuming nearly all available space.

Other useful MAT views:

  • Histogram: Tallies instances per class. Expect byte[] to rank highest.
  • Dominator Tree: Surfaces the largest retained objects and their dependents. The controller’s list appears at the top, referencing every allocated array.
  • Top Consumers: Aggregates memory by class and package to highlight heavy components.
  • Component Report: Analyzes retention for a specific package or class-loader boundary.

Opening the Dominator Tree confirms that HeapExhaustionEndpoint.payloadBuffer is the primary GC root path preventing collection, with each node representing a 2 MB byte allocation.

Tags: java Spring Boot JVM OutOfMemoryError Heap Dump

Posted on Wed, 27 May 2026 22:12:06 +0000 by tomtimms