Why MyBatis Triggers OutOfMemoryError and How to Fix It
The article examines a production OutOfMemoryError caused by MyBatis SQL construction, explains heap and metaspace exhaustion, analyzes MyBatis source code, reproduces the issue with JVM settings, and offers practical recommendations to prevent similar memory failures.
Introduction
After a recent CPU alarm, the service began throwing frequent OutOfMemoryError (OOM) in production, causing the distributed system to become unavailable. The operations team restarted the service to keep the B‑side product running while the issue was investigated.
Root Causes of OOM
OOM in Java typically stems from two sources: insufficient heap space and insufficient metaspace.
Heap exhaustion occurs when objects with strong references remain reachable, preventing GC from reclaiming memory and eventually exceeding the -Xmx limit.
Metaspace (introduced in Java 8) stores class metadata outside the heap; it can also run out if not sized properly, replacing the former PermGen.
Common Scenarios Leading to Heap OOM
Loading excessively large result sets from a database into memory.
Infinite loops that retain large objects, preventing GC.
Failure to release resources such as connection pools or I/O streams.
Static collections that keep references to objects indefinitely.
These are typical patterns, though real‑world issues can be more obscure.
Problem Analysis
Log inspection showed that MyBatis was generating a large amount of SQL and storing placeholders and parameter objects in a Map (ContextMap). When the SQL becomes very long, the Map grows dramatically, and under high concurrency the objects cannot be reclaimed, leading to heap OOM.
Because the Docker container lacked diagnostic tools like jstack or jmap, the author relied on online resources and source‑code inspection to pinpoint the issue.
MyBatis Source Code Insight
DynamicContext contains a ContextMap (extends HashMap) called bindings. ForEachSqlNode calls getBindings(), which puts SQL parameters and placeholders into this map. The map entries are strongly referenced, so they are not eligible for GC, and heavy concurrent queries cause memory pressure.
Reproducing the Issue
The author reproduced the problem locally by constructing a large IN clause, launching 50 threads to execute the statements, and running the JVM with -Xmx256m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError. The console showed frequent Full GC cycles and eventual OOM.
Conclusion and Recommendations
After identifying the root cause, the remedy is to optimize SQL construction—avoid overly large concatenated statements, limit the size of parameter collections, and ensure that temporary objects can be garbage‑collected. Properly sizing heap and metaspace, and reviewing MyBatis usage patterns, can prevent similar OOM incidents.
Author: Lxlxxx Source: juejin.cn/post/7221461552343072828
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
