Backend Development 11 min read

Measuring Heap Memory Allocation per HTTP and RPC Request in SpringBoot

This article details a practical experiment using SpringBoot 2.5.4 and JMeter to measure the heap memory allocated by individual HTTP and RPC requests, analyzes GC logs, and demonstrates how request size and logging affect memory consumption, providing insights for backend performance optimization.

Top Architect
Top Architect
Top Architect
Measuring Heap Memory Allocation per HTTP and RPC Request in SpringBoot

In this technical note, a senior architect explains how to quantify the heap memory consumption of a single HTTP or RPC request in a SpringBoot application, and how this information can be used to tune GC parameters and improve overall system performance.

1. Experiment Idea

The goal is to determine the exact amount of heap memory allocated for one HTTP call and one RPC call, then infer the number of GC cycles per minute under a given concurrency level. By measuring the memory allocated in the young generation, the frequency of young GC events can be estimated.

2. Test Setup

Create a new SpringBoot 2.5.4 project.

Add a POST /create endpoint for JMeter to invoke and a GET /gc endpoint that triggers a manual GC.

Configure JMeter to run 10 threads, each performing 2,000 calls (total 20,000 requests).

Enable detailed GC logging and record the memory allocated in the young generation before and after each GC.

3. SpringBoot HTTP Endpoint Declaration

@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";
    }
}

4. JMeter Test Plan

The test plan includes a thread group (10 threads, 2,000 loops), HTTP Request defaults, a JSON body for the create endpoint, and a header Content-Type: application/json . After the 20,000 calls, a manual GC is triggered via curl http://localhost:8080/gc to capture the final GC log.

5. JVM Configuration

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:+CMSScavengeBeforeRemark -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.jar

6. Experiment Process

The application runs with a 4 GB heap (2 GB young generation). Before the load test, a manual GC clears existing objects. JMeter then executes the 20,000 HTTP calls, after which another manual GC is performed to capture the total young‑generation memory growth.

7. Results

Even with a tiny request body (≈50 bytes), each HTTP call allocated roughly 34 KB of heap memory. Adding a larger detail field (≈1,200 characters) increased the allocation to about 36 KB , indicating that the overhead is dominated by SpringBoot’s internal processing rather than the payload size.

When detailed request logging was enabled, the per‑request memory usage rose to 56 KB . Removing the log entry reduced it back to 35.7 KB , showing that log volume can significantly affect memory consumption and GC frequency.

8. Conclusions

For a simple SpringBoot service, a single HTTP request consumes around 34 KB of heap memory, which can be considered a lower bound. In production systems with richer business logic, RPC calls can consume 0.5 MB–1 MB per request, leading to substantial memory pressure and frequent young GC cycles under high concurrency.

Controlling log size and understanding per‑request memory footprints are essential for scaling backend services efficiently.

9. Sample Log Output

{"userId": 32898493, "productId":39043, "detail": ""}

10. Logging Example

log.warn("Received order cnt{}:{}", count.getAndIncrement(), order);
JVMbackend developmentperformance testingSpringBootGCMemory Profiling
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.