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