Backend Development 12 min read

Root Cause Analysis of Excessive Swap Memory Usage in a Spring Boot Application

The article details a step‑by‑step investigation of abnormal swap memory consumption in a Spring Boot project, revealing that native memory allocated by the Inflater during JAR scanning was not released promptly, leading to apparent memory leaks that were ultimately resolved by configuring package scanning and updating Spring Boot.

Architect
Architect
Architect
Root Cause Analysis of Excessive Swap Memory Usage in a Spring Boot Application

Background: After migrating a project to the MDP framework (based on Spring Boot) a swap‑area usage alarm appeared. Although the JVM was configured with a 4 GB heap, the process’s resident memory reached 7 GB. Initial observations from top and jcmd VM.native_memory showed a large amount of native memory.

Investigation Process:

1. Using -XX:NativeMemoryTracking=detail and jcmd the native memory distribution was examined, indicating that the committed memory was lower than the physical usage, suggesting native allocations outside the JVM heap.

2. pmap revealed many 64 MB regions not accounted for by jcmd . These regions were traced to native code.

3. System‑level tools were employed:

gperftools showed a peak of ~3 GB native allocation that later settled around 700 MB.

strace on brk , mmap , and munmap did not capture suspicious calls during normal operation.

GDB was used to dump memory regions identified via /proc/pid/smaps .

jstack identified the thread performing the allocations.

The culprit turned out to be the Meituan Configuration Center (MCC) which uses the Reflections library to scan all JARs. Spring Boot’s ZipInflaterInputStream wraps Inflater to decompress JARs, allocating off‑heap memory that is only freed in the finalize method.

Because the finalizer runs after GC, the native memory remained allocated for a long time, and the underlying glibc memory allocator kept the pages in its per‑thread arena (64 MB chunks), giving the impression of a leak.

Solution:

Configure MCC to scan only specific packages, reducing the number of JARs processed.

Upgrade Spring Boot to a version where ZipInflaterInputStream explicitly releases native memory.

Additional Experiments:

A custom malloc implementation that uses mmap was built and preloaded via LD_PRELOAD to observe allocation behavior. The code is shown below:

#include<sys/mman.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>

void* malloc(size_t size) {
    long* ptr = mmap(0, size + sizeof(long), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if (ptr == MAP_FAILED) return NULL;
    *ptr = size;
    return (void*)(&ptr[1]);
}

void* calloc(size_t n, size_t size) {
    void* ptr = malloc(n * size);
    if (!ptr) return NULL;
    memset(ptr, 0, n * size);
    return ptr;
}

void* realloc(void* ptr, size_t size) {
    if (size == 0) { free(ptr); return NULL; }
    if (!ptr) return malloc(size);
    long* plen = (long*)ptr; plen--; long len = *plen;
    if (size <= len) return ptr;
    void* rptr = malloc(size);
    if (!rptr) { free(ptr); return NULL; }
    memcpy(rptr, ptr, len);
    free(ptr);
    return rptr;
}

void free(void* ptr) {
    if (!ptr) return;
    long* plen = (long*)ptr; plen--; long len = *plen;
    munmap((void*)plen, len + sizeof(long));
}

The experiments confirmed that native allocations stay in the process’s address space (700‑800 MB) while the OS reports a larger resident set due to the allocator’s arena retention.

Conclusion: The excessive swap usage was caused by off‑heap memory allocated during JAR scanning, retained by the glibc memory arena. Proper configuration of package scanning and upgrading Spring Boot to release the native memory explicitly resolves the issue.

JavaMemory LeakSpring BootNative MemoryPerformance Debugginggperftools
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.