Add flags: production JVM flag cheat sheet for Spring Boot on JDK 25/27
MaxRAMPercentage vs a hardcoded -Xmx, -XX:-UseContainerSupport, the ActiveProcessorCount lie, and which diagnostic flags are worth leaving on in prod, verified in real Docker containers on JDK 25 and JDK 27. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package com.ankurm.flagsdemo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Companion app for "JVM Flags for Spring Boot in Containers (JDK 25/27)" on ankurm.com.
|
||||
* See docs/output/*.txt for the captured runs this post quotes from, and
|
||||
* {@link JvmDiagnosticsController} for the live /internal/jvm-flags endpoint
|
||||
* used to inspect what the JVM actually resolved its memory/GC flags to
|
||||
* inside a running container. Delete that endpoint before shipping this to
|
||||
* a real service — it exists for this demo only.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class FlagsDemoApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(FlagsDemoApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.ankurm.flagsdemo;
|
||||
|
||||
import com.sun.management.HotSpotDiagnosticMXBean;
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Prints what the JVM actually resolved its container-relevant flags to, at
|
||||
* runtime, inside whatever cgroup it is running under. This is the live
|
||||
* version of what docs/output/*.txt captures from -XX:+PrintFlagsFinal; the
|
||||
* two should agree, and the post at ankurm.com cross-checks both.
|
||||
*
|
||||
* <p>Explained in the post:
|
||||
* https://ankurm.com/jvm-flags-for-spring-boot-in-containers-jdk-25-27/
|
||||
*
|
||||
* <p><strong>Delete this endpoint before shipping to a real service.</strong>
|
||||
* It leaks heap sizing and cgroup limits to anyone who can reach it.
|
||||
*/
|
||||
@RestController
|
||||
public class JvmDiagnosticsController {
|
||||
|
||||
private static final List<String> FLAGS_OF_INTEREST = List.of(
|
||||
"MaxRAMPercentage", "MinRAMPercentage", "InitialRAMPercentage",
|
||||
"MaxRAM", "MaxHeapSize", "InitialHeapSize", "MinHeapSize",
|
||||
"UseContainerSupport", "ActiveProcessorCount",
|
||||
"UseG1GC", "UseParallelGC", "UseZGC", "UseSerialGC",
|
||||
"UseCompactObjectHeaders", "ParallelGCThreads", "ConcGCThreads"
|
||||
);
|
||||
|
||||
@GetMapping("/internal/jvm-flags")
|
||||
public Map<String, Object> jvmFlags() {
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
|
||||
OperatingSystemMXBean os =
|
||||
(OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
||||
Map<String, Object> hostView = new LinkedHashMap<>();
|
||||
hostView.put("availableProcessors", Runtime.getRuntime().availableProcessors());
|
||||
hostView.put("totalMemorySizeBytes", os.getTotalMemorySize());
|
||||
hostView.put("freeMemorySizeBytes", os.getFreeMemorySize());
|
||||
out.put("osMXBeanView", hostView);
|
||||
|
||||
MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
|
||||
MemoryUsage heap = mem.getHeapMemoryUsage();
|
||||
Map<String, Object> heapView = new LinkedHashMap<>();
|
||||
heapView.put("initBytes", heap.getInit());
|
||||
heapView.put("usedBytes", heap.getUsed());
|
||||
heapView.put("committedBytes", heap.getCommitted());
|
||||
heapView.put("maxBytes", heap.getMax());
|
||||
out.put("heap", heapView);
|
||||
|
||||
List<String> gcNames = ManagementFactory.getGarbageCollectorMXBeans()
|
||||
.stream().map(GarbageCollectorMXBean::getName).toList();
|
||||
out.put("garbageCollectors", gcNames);
|
||||
|
||||
HotSpotDiagnosticMXBean diag =
|
||||
ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
|
||||
Map<String, String> flagView = new LinkedHashMap<>();
|
||||
for (String name : FLAGS_OF_INTEREST) {
|
||||
try {
|
||||
flagView.put(name, diag.getVMOption(name).getValue());
|
||||
} catch (IllegalArgumentException notThisJvm) {
|
||||
flagView.put(name, "N/A on this build");
|
||||
}
|
||||
}
|
||||
out.put("resolvedFlags", flagView);
|
||||
|
||||
out.put("jvmInputArguments",
|
||||
ManagementFactory.getRuntimeMXBean().getInputArguments());
|
||||
out.put("javaVersion", System.getProperty("java.version"));
|
||||
out.put("javaVmName", System.getProperty("java.vm.name"));
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: flags-demo
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
Reference in New Issue
Block a user