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.
*
*
Explained in the post:
* https://ankurm.com/jvm-flags-for-spring-boot-in-containers-jdk-25-27/
*
*
Delete this endpoint before shipping to a real service.
* It leaks heap sizing and cgroup limits to anyone who can reach it.
*/
@RestController
public class JvmDiagnosticsController {
private static final List 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 jvmFlags() {
Map out = new LinkedHashMap<>();
OperatingSystemMXBean os =
(OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
Map 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 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 gcNames = ManagementFactory.getGarbageCollectorMXBeans()
.stream().map(GarbageCollectorMXBean::getName).toList();
out.put("garbageCollectors", gcNames);
HotSpotDiagnosticMXBean diag =
ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
Map 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;
}
}