Add shenandoah-jdk28: generational vs SATB Shenandoah, G1 and ZGC on a Spring Boot service (JEP 535 status probe included)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HmvoHH3ZnCEaPNXRpDxDZe
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package com.ankurm.shenandoah;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* A server with the shape a generational collector is built for: a large, slowly changing live set
|
||||
* (the cache) plus a steady stream of short-lived garbage (each request).
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class ShenandoahApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ShenandoahApplication.class, args);
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class Api {
|
||||
|
||||
/** 200,000 slots of 1 KiB is a live set of roughly 210 MiB. */
|
||||
static final int SLOTS = 200_000;
|
||||
/** Slots replaced per request: the old value becomes old-generation garbage. */
|
||||
static final int REPLACED_PER_REQUEST = 8;
|
||||
/** Short-lived 1 KiB arrays allocated per request: about 100 KiB of young garbage. */
|
||||
static final int TEMPORARIES = 100;
|
||||
|
||||
private final AtomicReferenceArray<byte[]> cache = new AtomicReferenceArray<>(SLOTS);
|
||||
/** Written on every request so the JIT cannot discard the temporaries. */
|
||||
private volatile int sink;
|
||||
|
||||
Api() {
|
||||
for (int i = 0; i < SLOTS; i++) {
|
||||
cache.set(i, new byte[1024]);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/work")
|
||||
String work() {
|
||||
ThreadLocalRandom r = ThreadLocalRandom.current();
|
||||
for (int i = 0; i < REPLACED_PER_REQUEST; i++) {
|
||||
cache.set(r.nextInt(SLOTS), new byte[1024]);
|
||||
}
|
||||
byte[][] temp = new byte[TEMPORARIES][];
|
||||
for (int i = 0; i < TEMPORARIES; i++) {
|
||||
temp[i] = new byte[1024];
|
||||
temp[i][0] = (byte) i;
|
||||
}
|
||||
sink = temp[r.nextInt(TEMPORARIES)][0] + cache.get(r.nextInt(SLOTS))[0];
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@GetMapping("/info")
|
||||
Map<String, Object> info() {
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("javaVersion", System.getProperty("java.version"));
|
||||
m.put("committedMiB", rt.totalMemory() >> 20);
|
||||
m.put("maxMiB", rt.maxMemory() >> 20);
|
||||
m.put("gc", ManagementFactory.getGarbageCollectorMXBeans().stream().map(b -> b.getName()).toList());
|
||||
m.put("jvmUptimeMs", ManagementFactory.getRuntimeMXBean().getUptime());
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user