// Env.java -- prints everything a GC measurement needs in order to be reproducible. // // Run it (JDK 25 single-file source launch, no compile step needed): // // java env/Env.java // java -XX:+UseZGC -Xms2g -Xmx2g env/Env.java // java -XX:+UseG1GC -Xms2g -Xmx2g env/Env.java // // Why this file exists: a GC benchmark without the environment printed next to it // is an anecdote. Heap size, CPU count, GC selection and the *ergonomic* values the // JVM picked for you (SoftMaxHeapSize, region size, GC thread counts) all move the // numbers more than the code under test does. import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.RuntimeMXBean; public final class Env { public static void main(String[] args) { RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); System.out.println("=== Runtime ==="); System.out.printf("java.version : %s%n", System.getProperty("java.version")); System.out.printf("java.vm.name : %s%n", System.getProperty("java.vm.name")); System.out.printf("java.vm.version : %s%n", System.getProperty("java.vm.version")); System.out.printf("java.vendor : %s%n", System.getProperty("java.vendor")); System.out.printf("os.name / os.arch : %s / %s%n", System.getProperty("os.name"), System.getProperty("os.arch")); System.out.printf("availableProcessors : %d%n", Runtime.getRuntime().availableProcessors()); System.out.printf("maxMemory (-Xmx) : %s%n", mb(Runtime.getRuntime().maxMemory())); System.out.printf("totalMemory (now) : %s%n", mb(Runtime.getRuntime().totalMemory())); System.out.println(); System.out.println("=== Collector in use ==="); // On JDK 25 the ZGC bean names are "ZGC Major Cycles" / "ZGC Minor Cycles" (and the // matching "... Pauses" beans). The presence of BOTH a Minor and a Major bean is the // cheapest programmatic proof that you are running *generational* ZGC. boolean sawMinor = false, sawMajor = false; for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { System.out.printf(" bean: %-24s count=%-6d timeMs=%d%n", gc.getName(), gc.getCollectionCount(), gc.getCollectionTime()); String n = gc.getName().toLowerCase(); if (n.contains("minor") || n.contains("young")) sawMinor = true; if (n.contains("major") || n.contains("old") || n.contains("full")) sawMajor = true; } System.out.printf(" generational? : %s%n", (sawMinor && sawMajor) ? "yes (separate young + old cycles reported)" : "no / single cycle"); System.out.println(); System.out.println("=== Memory pools ==="); for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) { System.out.printf(" %-28s type=%-10s max=%s%n", pool.getName(), pool.getType(), mb(pool.getUsage().getMax())); } System.out.println(); System.out.println("=== JVM arguments actually in effect ==="); if (runtime.getInputArguments().isEmpty()) { System.out.println(" (none -- everything below is ergonomic)"); } runtime.getInputArguments().forEach(a -> System.out.println(" " + a)); System.out.println(); System.out.println("Tip: dump every ergonomic value the JVM chose with"); System.out.println(" java -XX:+UseZGC -Xms2g -Xmx2g -XX:+PrintFlagsFinal -version"); } private static String mb(long bytes) { if (bytes < 0) return "unbounded"; return String.format("%,d bytes (%.0f MB)", bytes, bytes / 1024.0 / 1024.0); } }