Backend Development 6 min read

Analyzing and Reproducing OutOfMemoryError in MyBatis-based Java Services

This article examines the causes of Java OutOfMemoryError in a distributed backend service, analyzes MyBatis-related memory leaks, demonstrates a reproducible scenario with large SQL concatenations and multithreading, and offers practical mitigation strategies to prevent heap and metaspace overflow.

Java Captain
Java Captain
Java Captain
Analyzing and Reproducing OutOfMemoryError in MyBatis-based Java Services

Preface

After a previous CPU alarm, the service soon encountered frequent OutOfMemoryError (OOM) in production logs, rendering the service unusable and causing widespread failures in Skywalking traces; the team restarted the service to restore business continuity while the root cause investigation began.

Reasons for OutOfMemoryError

OutOfMemoryError generally stems from two sources: insufficient heap space and insufficient metaspace.

Heap space shortage occurs when objects remain strongly referenced and cannot be garbage‑collected, eventually exceeding the -Xmx maximum heap size.

Metaspace, introduced in Java 8 to replace the permanent generation, resides off‑heap; it stores class metadata via pointers and can also overflow if not properly sized.

Common Heap Memory Overflow Scenarios

Loading excessively large query results into memory.

Infinite loops that keep large objects referenced.

Failure to manually release resources such as connection pools or I/O streams.

Static collections that retain object references indefinitely.

These are typical cases, though unusual issues can also cause OOM.

Phenomenon Analysis

Production logs show a MyBatis‑related memory overflow; MyBatis builds SQL strings using collection classes, and when SQL becomes large, the collections grow dramatically and cannot be reclaimed, leading to OOM.

Because the Docker container lacks tools like jstack or jmap and no heap dump was saved, detailed thread‑level analysis was impossible.

Online research eventually provided insight into the problem.

The referenced article describes how MyBatis stores generated SQL and parameters in a Map; with many parameters and concurrent threads, the Map retains large objects, causing high memory usage and OOM.

MyBatis Source Code Analysis

Inspecting DynamicContext reveals a ContextMap (extends HashMap ) that holds bindings. ForEachSqlNode puts SQL parameters and placeholders into this map, preventing garbage collection under heavy concurrent queries, which leads to OOM.

Scenario Reproduction

To reproduce the issue, the SQL IN clause was enlarged, 50 threads were launched, and the JVM was started with -Xmx256m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError .

The console logs show frequent Full GC cycles, ultimately leading to OOM.

Conclusion

After identifying the root cause, the remedy is to optimize SQL generation—avoid excessively large concatenated statements, limit parameter counts, and ensure proper resource cleanup—to prevent heap and metaspace exhaustion in Java backend services.

backendJavaperformancememory-leakMyBatisOutOfMemoryError
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

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.