Root Cause Analysis of OOM in a Spring Boot Service: ScriptEngine Initialization and StringSequence Memory Consumption
This article details a step‑by‑step investigation of an OutOfMemoryError in a Spring Boot social app, revealing that frequent initialization of a script engine caused massive StringSequence instances via SPI loading, and shows how consolidating the engine eliminated the OOM issue.
During the operation of a Spring Boot social application, developers repeatedly encountered OutOfMemoryError (OOM) symptoms such as missing large‑memory objects in code, inability to locate certain classes, and differing behavior between local and server launches.
The service processes real‑time data using multithreaded asynchronous execution; after increasing the test environment memory from 4 GB to 8 GB the problem persisted, prompting a systematic OOM investigation.
Initial memory diagnostics with jmap -heap 44 and jstat -gcutil 6556 1000 100 showed 100 % usage of the From Space and high CMS generation usage, indicating severe heap pressure rather than a classic leak, leading the team to focus on excessive object creation.
Log‑related memory was examined via jmap -histo 3688 | more ; adjusting the async logger ring buffer size removed the logger queue but did not resolve the OOM, suggesting deeper causes.
A mysterious class named StringSequence appeared in heap dumps; it was not present in the source code and was identified as a Spring Boot internal class created during fat‑jar class‑path scanning.
Different startup modes (IDE classpath vs. fat‑jar) were compared, confirming that the fat‑jar launch recreated the StringSequence instances, linking the issue to the class‑loader behavior.
Further analysis using jmap -dump:live,format=b,file=dump.hprof 21896 and jhat revealed that each StringSequence held a source field pointing to a javax.script.ScriptEngineFactory resource, indicating script engine involvement.
Arthas was employed to trace the initialization chain with a stack command targeting the StringSequence constructor, uncovering a loop that repeatedly instantiated a script engine, causing the memory blow‑up.
Deep analysis highlighted three memory‑risk factors: (1) SPI loading of ScriptEngineFactory traverses every JAR, creating many StringSequence objects; (2) each script engine (Nashorn) allocates numerous internal structures (e.g., 126 fields in its global object); (3) repeated engine creation triggers massive dynamic class generation, stressing the GC.
The fix was to make the script engine globally reusable instead of creating it per request, which eliminated the StringSequence proliferation and resolved the OOM problem, as confirmed by subsequent monitoring.
The case study emphasizes systematic memory diagnostics, the importance of understanding Spring Boot’s class‑loader mechanics, and careful management of dynamic script engines in backend services.
Zhuanzhuan Tech
A platform for Zhuanzhuan R&D and industry peers to learn and exchange technology, regularly sharing frontline experience and cutting‑edge topics. We welcome practical discussions and sharing; contact waterystone with any questions.
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.