Add jdk28-zgc: ZGC startup and warmup baseline on JDK 25/27/28-ea, and a probe for JEP 545/546
Spring Boot 4.1.1 harness measuring first-response time versus initial heap size and a 20,000-request warmup. Shows -XX:+ZAdaptiveHeapSizing is not yet in any build. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WzVQ6zFh9cuLPj9RSbRH8U
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import java.net.URI;
|
||||
import java.net.http.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
/** Fires TOTAL requests with THREADS threads and reports how long each batch of BATCH took. */
|
||||
public class Warmup {
|
||||
public static void main(String[] a) throws Exception {
|
||||
int port = Integer.parseInt(a[0]);
|
||||
int total = 20_000, batch = 2_000, threads = 4;
|
||||
HttpClient c = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
|
||||
HttpRequest rq = HttpRequest.newBuilder(URI.create("http://localhost:" + port + "/hello")).build();
|
||||
AtomicInteger done = new AtomicInteger();
|
||||
long[] batchEnd = new long[total / batch];
|
||||
long t0 = System.nanoTime();
|
||||
ExecutorService ex = Executors.newFixedThreadPool(threads);
|
||||
List<Future<?>> fs = new ArrayList<>();
|
||||
for (int t = 0; t < threads; t++) fs.add(ex.submit(() -> {
|
||||
try {
|
||||
while (true) {
|
||||
int n = done.incrementAndGet();
|
||||
if (n > total) return null;
|
||||
c.send(rq, HttpResponse.BodyHandlers.discarding());
|
||||
if (n % batch == 0) batchEnd[n / batch - 1] = System.nanoTime();
|
||||
}
|
||||
} catch (Exception e) { throw new RuntimeException(e); }
|
||||
}));
|
||||
for (Future<?> f : fs) f.get();
|
||||
ex.shutdown();
|
||||
StringBuilder sb = new StringBuilder("batch ms:");
|
||||
long prev = t0;
|
||||
for (long e : batchEnd) { sb.append(' ').append((e - prev) / 1_000_000); prev = e; }
|
||||
System.out.println(sb);
|
||||
System.out.println("total ms: " + (prev - t0) / 1_000_000);
|
||||
}
|
||||
}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerates every file in output/. Set J25, J27, J28 to the three JDK home directories.
|
||||
# Timing results drift between runs and machines; treat them as indicative, not exact.
|
||||
set -u
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
OUT="$HERE/../output"; mkdir -p "$OUT"
|
||||
: "${J25:?}" "${J27:?}" "${J28:?}"
|
||||
export JDK_CLIENT="$J25"
|
||||
RUNS=${RUNS:-5}
|
||||
|
||||
# 01 - what does each JDK say about the ZGC flags the two JEPs talk about?
|
||||
{
|
||||
for p in "25|$J25" "27|$J27" "28-ea|$J28"; do
|
||||
v=${p%%|*}; h=${p#*|}
|
||||
echo "== JDK $v"
|
||||
"$h/bin/java" -version 2>&1 | grep -v Picked | sed -n 1p
|
||||
"$h/bin/java" -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:+PrintFlagsFinal -version 2>&1 \
|
||||
| grep -E ' (InitialHeapSize|MaxHeapSize|SoftMaxHeapSize|ZUncommit|ZUncommitDelay) ' | sed 's/ \{2,\}/ /g'
|
||||
echo "-- probing -XX:+ZAdaptiveHeapSizing"
|
||||
"$h/bin/java" -XX:+UseZGC -XX:+ZAdaptiveHeapSizing -version 2>&1 | grep -v Picked | sed -n 1p
|
||||
echo
|
||||
done
|
||||
} > "$OUT/01-flag-probe.txt"
|
||||
|
||||
# 02 - startup versus initial heap size
|
||||
{
|
||||
for p in "jdk25|$J25" "jdk27|$J27" "jdk28ea|$J28"; do
|
||||
l=${p%%|*}; h=${p#*|}
|
||||
"$HERE/startup.sh" "$h" "$l zgc default" "$RUNS" -XX:+UseZGC
|
||||
"$HERE/startup.sh" "$h" "$l zgc Xms64m" "$RUNS" -XX:+UseZGC -Xms64m
|
||||
"$HERE/startup.sh" "$h" "$l zgc Xms1g" "$RUNS" -XX:+UseZGC -Xms1g
|
||||
"$HERE/startup.sh" "$h" "$l zgc Xms3g" "$RUNS" -XX:+UseZGC -Xms3g
|
||||
"$HERE/startup.sh" "$h" "$l zgc Xms3g pretouch" "$RUNS" -XX:+UseZGC -Xms3g -XX:+AlwaysPreTouch
|
||||
done
|
||||
} > "$OUT/02-startup-vs-xms.txt" 2>&1
|
||||
|
||||
# 03 - warmup: how long do the first 20,000 requests take?
|
||||
{
|
||||
for p in "jdk25|$J25" "jdk28ea|$J28"; do
|
||||
l=${p%%|*}; h=${p#*|}
|
||||
"$HERE/warmup.sh" "$h" "$l zgc default" -XX:+UseZGC
|
||||
"$HERE/warmup.sh" "$h" "$l zgc Xms3g" -XX:+UseZGC -Xms3g
|
||||
"$HERE/warmup.sh" "$h" "$l zgc Xms64m" -XX:+UseZGC -Xms64m
|
||||
done
|
||||
} > "$OUT/03-warmup.txt" 2>&1
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Usage: startup.sh <java-home> <label> <runs> [jvm args...]
|
||||
# Starts the Spring Boot jar, polls /hello until HTTP 200, and prints wall-clock ms from
|
||||
# process start to first response plus the committed heap one second later. One line per run,
|
||||
# then a summary line (min / median / max).
|
||||
set -u
|
||||
JH=$1; LABEL=$2; RUNS=$3; shift 3
|
||||
JAR="$(dirname "$0")/../target/jdk28-zgc-1.0.0.jar"
|
||||
PORT=${PORT:-18080}
|
||||
times=()
|
||||
for r in $(seq 1 "$RUNS"); do
|
||||
t0=$(date +%s%N)
|
||||
"$JH/bin/java" "$@" -jar "$JAR" --server.port=$PORT --spring.main.banner-mode=off \
|
||||
--logging.level.root=WARN > /dev/null 2>&1 &
|
||||
PID=$!
|
||||
while :; do
|
||||
code=$(curl -s -o /dev/null -m 1 -w '%{http_code}' "localhost:$PORT/hello")
|
||||
[ "$code" = 200 ] && break
|
||||
kill -0 $PID 2>/dev/null || { echo "$LABEL run $r: JVM exited early"; break; }
|
||||
sleep 0.02
|
||||
done
|
||||
t1=$(date +%s%N)
|
||||
ms=$(( (t1 - t0) / 1000000 ))
|
||||
sleep 1
|
||||
committed=$(curl -s "localhost:$PORT/heap" | sed -n 's/.*"committedMiB":\([0-9]*\).*/\1/p')
|
||||
echo "$LABEL run $r: first-response ${ms} ms, committed heap ${committed} MiB"
|
||||
times+=("$ms")
|
||||
kill $PID 2>/dev/null; wait $PID 2>/dev/null
|
||||
done
|
||||
sorted=($(printf '%s\n' "${times[@]}" | sort -n))
|
||||
n=${#sorted[@]}
|
||||
echo "$LABEL SUMMARY: min ${sorted[0]} ms, median ${sorted[$((n/2))]} ms, max ${sorted[$((n-1))]} ms (n=$n)"
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
# Usage: warmup.sh <java-home> <label> [jvm args...]
|
||||
# Starts the server, waits for the first response, then runs Warmup.java (always on the same
|
||||
# client JDK, JDK_CLIENT) so only the server JVM changes between runs.
|
||||
set -u
|
||||
JH=$1; LABEL=$2; shift 2
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
JAR="$HERE/../target/jdk28-zgc-1.0.0.jar"
|
||||
PORT=${PORT:-18080}
|
||||
CLIENT=${JDK_CLIENT:-$JH}
|
||||
"$JH/bin/java" "$@" -jar "$JAR" --server.port=$PORT --spring.main.banner-mode=off \
|
||||
--logging.level.root=WARN > /dev/null 2>&1 &
|
||||
PID=$!
|
||||
until [ "$(curl -s -o /dev/null -m 1 -w '%{http_code}' localhost:$PORT/hello)" = 200 ]; do sleep 0.02; done
|
||||
echo "== $LABEL"
|
||||
"$CLIENT/bin/java" "$HERE/Warmup.java" $PORT 2>&1 | grep -v Picked
|
||||
curl -s "localhost:$PORT/heap" | sed -n 's/.*"committedMiB":\([0-9]*\),"maxMiB":\([0-9]*\).*/committed after load: \1 MiB (max \2 MiB)/p'; echo
|
||||
kill $PID 2>/dev/null; wait $PID 2>/dev/null
|
||||
Reference in New Issue
Block a user