Measuring Per‑Request Heap Memory Usage in SpringBoot with JMeter and GC Logs
This guide explains how to determine the heap memory consumption of individual HTTP and RPC calls in a SpringBoot application by setting up a JMeter load test, enabling detailed GC logging, and analyzing the results to optimize JVM parameters and improve performance.
In real‑world work, full‑link pressure testing, GC parameter tuning, and JVM memory allocation optimization are often required. Knowing the heap memory needed for a single RPC or HTTP request allows precise calculation of total heap size for a given concurrency and estimation of GC frequency.
Experiment Design
Create a new SpringBoot application (version 2.5.4).
Add a POST endpoint /create for JMeter to call.
Configure a JMeter test plan: 10 threads, each executing 2000 HTTP calls (total 20,000 calls).
Enable detailed GC logging in SpringBoot to record memory allocated in the young generation before and after GC.
After the 20,000 HTTP calls, trigger a manual GC and calculate the total young‑generation memory increase, which reflects the memory requested by the HTTP calls.
SpringBoot HTTP Interface
@Slf4j
@RestController
public class TestController {
private AtomicLong count = new AtomicLong(0);
@ResponseBody
@RequestMapping(value = "create", method = RequestMethod.POST)
public String create(@RequestBody Order order) {
//log.warn("Received order cnt{}:{}", count.getAndIncrement(), order);
return "ok";
}
@ResponseBody
@RequestMapping(value = "gc", method = RequestMethod.GET)
public String gc() {
System.gc();
return "ok";
}
}JMeter Test Plan
3.1 Add Thread Group
Configure 10 threads, each looping 2000 times.
3.2 Add HTTP Defaults
3.3 Add Request Header
Since the request body is JSON, add the header Content-Type: application/json.
3.4 Add HTTP Request
Specify the URL and request body.
Running the Experiment
4.1 Start SpringBoot Application
Heap size 4G, young generation 2G, SurvivorRatio=8 (each survivor occupies 1/10 of the young generation).
Specify GC log location:
-Xloggc:/Users/testUser/log/gc.log java -server
-Xmx4g -Xms4g -XX:SurvivorRatio=8 -Xmn2g
-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -XX:MaxDirectMemorySize=1g
-XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCCause -XX:+PrintGCDetails
-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution
-XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=32768
-XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:ParallelCMSThreads=6 -XX:+CMSClassUnloadingEnabled
-XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelInitialMarkEnabled
-XX:+CMSParallelRemarkEnabled -XX:+PrintHeapAtGC
-XX:CMSFullGCsBeforeCompaction=1 -XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintReferenceGC
-XX:+ParallelRefProcEnabled -XX:ReservedCodeCacheSize=256M
-Xloggc:/Users/testUser/log/gc.log
-jar target/activiti-0.0.1-SNAPSHOT.jar4.2 Manual GC Before Test
Run a manual GC to clear existing objects:
curl http://localhost:8080/gc4.3 Execute JMeter Load Test
Run the JMeter plan, which invokes the HTTP endpoint 20,000 times.
4.4 GC Log Analysis
After GC, the Eden space usage drops to 0. The memory used before GC equals the total memory requested by the 20,000 HTTP calls.
Results
Even with a tiny request body (≈50 characters), each HTTP call consumes about 34 KB of heap memory, indicating that SpringBoot creates multiple internal objects beyond the payload size.
When the detail field is expanded to 1,200 characters, the average memory per call rises to 36 KB , a 2 KB increase matching the payload size.
Adding a log statement inside the controller increases the average memory consumption to 56 KB , an extra 20 KB, showing that larger logs significantly raise memory usage and may trigger more frequent GC.
Removing the detail field drops the memory usage back to 35.7 KB , confirming the impact of log size.
Recommendation: keep individual log entries small to reduce heap consumption and improve overall system performance.
Real‑World Data
In production, a single RPC request can consume between 0.5 MB and 1 MB of heap due to complex business logic, multiple downstream calls, database accesses, cache interactions, and extensive logging.
For example, with 0.5 MB per request and 500 concurrent requests per second, the system would allocate roughly 15 GB of memory per minute, requiring at least three young‑generation GCs each minute.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.
