From 25efd5d8d6da0046d3b657865681f95d06016f30 Mon Sep 17 00:00:00 2001 From: vanshslathia Date: Sun, 26 Oct 2025 23:10:12 +0530 Subject: [PATCH] feat(jep379): Implement Shenandoah GC demo and update JDK15 metadata --- src/main/java/org/javademos/init/Java15.java | 4 + .../java15/jep379/ShenandoahJEP379.java | 89 +++++++++++++++++++ src/main/resources/JDK15Info.json | 8 ++ 3 files changed, 101 insertions(+) create mode 100644 src/main/java/org/javademos/java15/jep379/ShenandoahJEP379.java diff --git a/src/main/java/org/javademos/init/Java15.java b/src/main/java/org/javademos/init/Java15.java index d4f8299d..475f111f 100644 --- a/src/main/java/org/javademos/init/Java15.java +++ b/src/main/java/org/javademos/init/Java15.java @@ -9,6 +9,7 @@ import org.javademos.java15.jep372.NashornRemovalDemo; import org.javademos.java15.jep373.DatagramSocketDemo15; import org.javademos.java15.jep375.InstanceofPatternMatchingSecondPreview; +import org.javademos.java15.jep379.ShenandoahJEP379; import org.javademos.java15.jep381.SolarisSparcRemovalDemo; import org.javademos.java15.jep383.ForeignMemoryAccessDemo; import org.javademos.java15.jep384.RecordsSecondPreviewDemo; @@ -34,12 +35,15 @@ public static ArrayList getDemos() { java15DemoPool.add(new DatagramSocketDemo15()); // JEP 375 java15DemoPool.add(new InstanceofPatternMatchingSecondPreview()); + // JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector (Production) + java15DemoPool.add(new ShenandoahJEP379()); // JEP 381 java15DemoPool.add(new SolarisSparcRemovalDemo()); // JEP 383 java15DemoPool.add(new ForeignMemoryAccessDemo()); // JEP 384 java15DemoPool.add(new RecordsSecondPreviewDemo()); + return java15DemoPool; } diff --git a/src/main/java/org/javademos/java15/jep379/ShenandoahJEP379.java b/src/main/java/org/javademos/java15/jep379/ShenandoahJEP379.java new file mode 100644 index 00000000..556841d3 --- /dev/null +++ b/src/main/java/org/javademos/java15/jep379/ShenandoahJEP379.java @@ -0,0 +1,89 @@ +package org.javademos.java15.jep379; + +import org.javademos.commons.IDemo; +import java.util.ArrayList; +import java.util.List; + +/** + * ## ☕ JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector (Production) + * + * This JEP finalized the Shenandoah garbage collector, making it a production feature + * in JDK 15. This meant the `-XX:+UnlockExperimentalVMOptions` flag was no longer + * required to use it. + * + * Shenandoah is a **latency-focused** GC that performs most of its work, including + * heap compaction, concurrently with the application threads. + * + * ### Key Benefits: + * - **Low Pause Times:** GC pause times are consistently very short (often < 10ms). + * - **Heap Size Independence:** Pause times are largely independent of the heap size. + * This makes it ideal for applications with very large heaps (hundreds of GBs). + * + * ### History: + * - **JEP 189 (JDK 12):** Shenandoah was first introduced as an *Experimental* feature. + * - **JEP 379 (JDK 15):** Promoted Shenandoah to a *Production* feature. + * + * ### How to Run the Demo: + * Since this feature is configured via JVM arguments, the demo primarily demonstrates + * how to force some GC activity and prints the required flags. + * + * To run the main application with Shenandoah, you only need: + * `java -XX:+UseShenandoahGC -Xms... -Xmx... -jar your-app.jar` + * + * @see JEP 379 + * @see JEP 189 (Initial Introduction) + */ +public class ShenandoahJEP379 implements IDemo { + + private static final int ALLOC_SIZE = 100_000_000; // Allocate 100MB per step + private static final int GC_CYCLES = 5; // Number of cycles to run + + @Override + public void demo() { + info(379); + + System.out.println("Shenandoah GC is primarily configured with JVM flags, not code."); + System.out.println("--------------------------------------------------------------------------------------------------"); + System.out.println("To use this collector (on a Java 15+ compatible JDK):"); + System.out.println("JVM Flags: -XX:+UseShenandoahGC -Xms4G -Xmx4G -Xlog:gc*=info:file=tmp/shenandoah_gc.log"); + System.out.println(" - NOTE: -Xms and -Xmx are recommended to be the same size."); + System.out.println("--------------------------------------------------------------------------------------------------"); + + try { + System.out.println("Simulating a memory-stressing workload to trigger GC activity..."); + + // Allocate objects to force GC cycles + List allocationList = new ArrayList<>(); + for (int i = 0; i < GC_CYCLES; i++) { + System.out.printf("Cycle %d: Allocating %d bytes (%.2fMB)...%n", + i + 1, ALLOC_SIZE, ALLOC_SIZE / 1024.0 / 1024.0); + + // Keep some objects alive (e.g., in a list) + allocationList.add(new byte[ALLOC_SIZE]); + + // Allow some time for concurrent GC to run + Thread.sleep(100); + } + + System.out.println("\nWorkload finished. Note: Shenandoah attempts to run concurrently."); + System.out.println("The short pauses are only visible when running with GC logging enabled."); + System.out.printf("Final allocated size kept alive: %.2fMB%n", + allocationList.size() * ALLOC_SIZE / 1024.0 / 1024.0); + + // Important: Call System.gc() to try and trigger a full GC. + // The logs will show if Shenandoah runs a concurrent cycle or a Stop-The-World (STW) full GC. + System.out.println("\nCalling System.gc()... (Shenandoah typically runs a concurrent cycle)"); + System.gc(); + + // Clear the list to allow all memory to be reclaimed + allocationList.clear(); + System.out.println("Cleared allocation list."); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (OutOfMemoryError e) { + System.err.println("!!! ERROR: Demo ran out of memory. Try increasing -Xmx size in your VM options. !!!"); + } + + } +} \ No newline at end of file diff --git a/src/main/resources/JDK15Info.json b/src/main/resources/JDK15Info.json index 829e86f2..61ac2206 100644 --- a/src/main/resources/JDK15Info.json +++ b/src/main/resources/JDK15Info.json @@ -47,6 +47,14 @@ "link": true, "code": false }, + { + "jep": 379, + "jdk": 15, + "name": "JEP 379 - Shenandoah: A Low-Pause-Time Garbage Collector", + "dscr": "Promoted Shenandoah from an experimental to a production feature.", + "link": false, + "code": true + }, { "jep": 381, "jdk": 15,