Container RAM Limit = [ JVM (Heap + Metaspace + Code Cache + Direct Mem) ]
+ [ OS & Native Overhead + Threads + Ignite Native Allocation]
|
Properly configuring memory for Broadleaf Microservices is critical for stability and performance in production environments. Under-allocating container RAM relative to JVM settings will lead to OOMKills, while over-allocating wastes expensive cloud resources. |
Total container memory (RSS) is the sum of several distinct JVM and OS memory regions. It is not enough to simply set the Heap; you must account for the "off-heap" overhead that the JVM requires to operate.
Container RAM Limit = [ JVM (Heap + Metaspace + Code Cache + Direct Mem) ]
+ [ OS & Native Overhead + Threads + Ignite Native Allocation]
Max Heap (-Xmx): The primary space for application objects. In Broadleaf, this includes the On-Heap Cache (Caffeine).
Metaspace: Native memory for class metadata. The MaxMetaspaceSize must include both the Compressed Class Space (Klass structures) and the Non-Compressed Space (bytecode, constant pools).
Code Cache: Native memory used by the JIT compiler to store compiled code.
Direct Memory: Native memory used for NIO buffers, Spring WebFlux (Netty) data processing, and Kafka clients. In Broadleaf, this also houses the Off-Heap Cache (Ehcache) when enabled.
Threads: Native memory allocated for Thread Stacks. Each thread consumes ~1MB of RAM outside the Heap. High-concurrency services (400-800 threads) require significant container overhead for these stacks.
OS & Native Overhead: A safety buffer for the operating system, GC management structures, and native library overhead.
The following heuristics are derived from reference baselines. Values will vary based on use cases (especially heap and cache sizes). Use as a general guide, rather than direct copy-paste data. Values are in Megabytes (MB) unless otherwise specified. If fine tuning a pre-existing production implemention, refer to the Production Analysis & Fine-Tuning section.
Caffeine operates entirely within the JVM heap. The Max Heap value is the result of the base application memory plus the dedicated Caffeine budget.
| Service Profile | Max Heap (Base + Cache) | Metaspace (Non-Comp + Comp) | Code Cache | Direct Mem | Container Limit (JVM + OS + Threads) |
|---|---|---|---|---|---|
Gateway |
1536 + 100 = 1636 |
256 + 256 = 512 |
240 |
2048 |
4436 + 256 + 100 = ~4.7 GB |
Auth |
2048 + 500 = 2548 |
512 + 256 = 768 |
240 |
2048 |
5604 + 256 + 400 = ~6.2 GB |
Browse / Cart |
3072 + 1000 = 4072 |
768 + 256 = 1024 |
240 |
2048 |
7384 + 256 + 700 = ~8.2 GB |
Processing |
3072 + 1000 = 4072 |
768 + 256 = 1024 |
240 |
2048 |
7384 + 256 + 700 = ~8.2 GB |
Supporting |
3072 + 1000 = 4072 |
768 + 256 = 1024 |
240 |
2048 |
7384 + 256 + 700 = ~8.2 GB |
All-in-One (Core) |
3072 + 1000 = 4072 |
768 + 256 = 1024 |
240 |
2048 |
7384 + 256 + 800 = ~8.3 GB |
Base Direct Memory: Set to a flat 2048 MB to support high-throughput NIO and networking operations.
Base Heap Note: The Base values are heuristic examples and must be validated through testing.
Technical Nuance:
When using Caffeine, the Direct Memory allocation is purely for standard JVM NIO buffers (and friends). The Container Limit is relatively lean because the cache does not require any additional "off-heap" buffers or native memory expansions.
JAVA_OPTS by Profile:
Gateway: -Xmx1636m -XX:MaxMetaspaceSize=512m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2048m
Auth: -Xmx2548m -XX:MaxMetaspaceSize=768m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2048m
Core / Flex: -Xmx4072m -XX:MaxMetaspaceSize=1024m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2048m
Ehcache reduces heap pressure by moving the majority of cached data into JVM Direct Memory.
| Service Profile | Max Heap (Base + Cache) | Metaspace (Non-Comp + Comp) | Direct Mem (Base + Cache) | Container Limit (JVM + OS + Threads) |
|---|---|---|---|---|
Gateway |
1536 + 100 = 1636 |
256 + 256 = 512 |
2048 + 0 = 2048 |
4436 + 256 + 100 = ~4.7 GB |
Auth |
2048 + 100 = 2148 |
512 + 256 = 768 |
2048 + 400 = 2448 |
5604 + 256 + 400 = ~6.2 GB |
Browse / Cart |
3072 + 300 = 3372 |
768 + 256 = 1024 |
2048 + 700 = 2748 |
7384 + 256 + 700 = ~8.2 GB |
Processing |
3072 + 300 = 3372 |
768 + 256 = 1024 |
2048 + 700 = 2748 |
7384 + 256 + 700 = ~8.2 GB |
Supporting |
3072 + 300 = 3372 |
768 + 256 = 1024 |
2048 + 700 = 2748 |
7384 + 256 + 700 = ~8.2 GB |
All-in-One (Core) |
3072 + 300 = 3372 |
768 + 256 = 1024 |
2048 + 700 = 2748 |
7384 + 256 + 800 = ~8.3 GB |
Total Direct Memory: Calculated as [Base Buffer (2048 MB)] + [Off-Heap Cache Budget].
Base Heap Note: The Base values are heuristic examples and must be validated through testing.
Technical Nuance:
Notice that the total Container Limit remains consistent with Caffeine for most services. The difference is the internal allocation: Max Heap is lowered to free up space for the expanded Direct Mem budget. This ensures that the high-performance serialization buffers used by Ehcache have dedicated space outside the primary heap.
JAVA_OPTS by Profile:
Gateway: -Xmx1636m -XX:MaxMetaspaceSize=512m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2048m
Auth: -Xmx2148m -XX:MaxMetaspaceSize=768m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2448m
Core / Flex: -Xmx3372m -XX:MaxMetaspaceSize=1024m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2748m
Ignite bypasses JVM memory management entirely for its cache, utilizing raw OS native memory.
| Service Profile | Max Heap (Base + 0) | Metaspace (Non-Comp + Comp) | Direct Mem | Container Limit (JVM + OS + Threads + Ignite) |
|---|---|---|---|---|
Gateway |
1536 + 0 = 1536 |
256 + 256 = 512 |
2048 |
4336 + 256 + 100 + 1280 = ~5.9 GB |
Auth |
2048 + 0 = 2048 |
512 + 256 = 768 |
2048 |
5104 + 256 + 400 + 1280 = ~6.9 GB |
Browse / Cart |
3072 + 0 = 3072 |
768 + 256 = 1024 |
2048 |
6384 + 256 + 700 + 1280 = ~8.5 GB |
Processing |
3072 + 0 = 3072 |
768 + 256 = 1024 |
2048 |
6384 + 256 + 700 + 1280 = ~8.5 GB |
Supporting |
3072 + 0 = 3072 |
768 + 256 = 1024 |
2048 |
6384 + 256 + 700 + 1280 = ~8.5 GB |
All-in-One (Core) |
3072 + 0 = 3072 |
768 + 256 = 1024 |
2048 |
6384 + 256 + 800 + 1280 = ~8.6 GB |
Base Direct Memory: Set to a flat 2048 MB to support high-throughput NIO and networking operations.
Base Heap Note: The Base values are heuristic examples and must be validated through testing.
Technical Nuance:
Ignite requires a flat +1280MB addition to the Container Limit to account for its native memory region, regardless of the service profile. This is invisible to standard JVM metrics but essential for K8S pod limits. Using Ignite for lightweight services (like the Gateway) results in a much larger container footprint compared to Caffeine or Ehcache.
JAVA_OPTS by Profile:
Gateway: -Xmx1536m -XX:MaxMetaspaceSize=512m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2048m
Auth: -Xmx2048m -XX:MaxMetaspaceSize=768m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2048m
Core / Flex: -Xmx3072m -XX:MaxMetaspaceSize=1024m -XX:CompressedClassSpaceSize=256m -XX:ReservedCodeCacheSize=240m -XX:MaxDirectMemorySize=2048m
While the heuristics provided in this guide serve as a starting point for green-field efforts, production environments require iterative tuning based on real-world data. If you are already in production, you should audit your current settings by comparing live telemetry against the Memory Equation.
Open the Broadleaf JVM Dashboard (or similar). Focus on the "Steady State"—the metrics where the application settles after several hours of typical production traffic. You are looking for where the application "sits" within its allocated spaces.
Compare your dashboard observations against the core equation components:
Container RAM Limit = [ JVM (Heap + Metaspace + Code Cache + Direct Mem) ] + [ OS & Native Overhead + Threads ]
Observe the Heap Memory panel. It distinguishes between raw application objects and memory managed by the Broadleaf Budget Manager (Caffeine).
Observation: Your "Base Heap" (Total Used Heap minus Cache Used) should ideally sit between 50% and 60% of your total allocated -Xmx during normal load.
Fine-Tune: If the Base Heap is consistently above 75%, increase -Xmx to avoid GC pressure. If it is consistently below 40%, you may be over-provisioned.
Observe the Non-Heap Memory panel, specifically Metaspace and Compressed Class Space.
Observation: These regions should plateau after the application is "warm."
Fine-Tune: Metaspace OOMs are fatal and often difficult to diagnose. If usage is within 10% of MaxMetaspaceSize, increase the limit. Conversely, if you have allocated 1GB but only use 300MB, you can safely reclaim that memory for the Heap.
Observe the Direct Memory panel. This is critical for Gateways and services using Ehcache.
Observation: This tracks NIO buffers (used by Netty/WebFlux) and off-heap cache segments.
Fine-Tune: Ensure MaxDirectMemorySize exceeds the observed "Used" line by at least 25%. Under-provisioning here often manifests as silent networking stalls or Direct buffer memory errors.
Observe the Thread Count panel.
Observation: Each thread consumes approximately 1MB of native RAM outside of the JVM Heap/Metaspace.
Fine-Tune: If a service (like Browse or Cart) settles at 800 threads, you must account for an additional ~800MB of RSS in your Container Limit that is not reflected in any JVM-specific memory pool.
The ultimate indicator of a well-tuned memory profile is GC Pressure (the percentage of time the CPU spends on Garbage Collection vs. application logic).
The Signal: If GC Pressure exceeds 5-10%, your tuning is likely inefficient.
The Fix: High pressure usually indicates a Heap that is too small for the current throughput or excessive object churn. Use the dashboard to identify if the pressure is in the "Young" or "Old" generation to determine if you need more raw Heap or more aggressive cache eviction policies.