Files
zgc-jdk25-benchmarks/flags/scripts/run-all.sh
T
asmhatreandClaude Sonnet 5 2f88734f5d Add flags: production JVM flag cheat sheet for Spring Boot on JDK 25/27
MaxRAMPercentage vs a hardcoded -Xmx, -XX:-UseContainerSupport, the
ActiveProcessorCount lie, and which diagnostic flags are worth leaving
on in prod, verified in real Docker containers on JDK 25 and JDK 27.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-10-01 04:34:04 +00:00

108 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Regenerates every file under docs/output/. Requires Docker (a running daemon)
# and network access to pull eclipse-temurin:25-jre, eclipse-temurin:25-jdk, and
# ubuntu:24.04, plus ~280MB to download the Temurin 27 GA tarball (no official
# eclipse-temurin:27-* image exists on Docker Hub as of 2026-10-01, which is why
# docker/Dockerfile.jdk27 installs the tarball directly).
set -euo pipefail
cd "$(dirname "$0")/.."
echo "== building images =="
mvn -q -B clean package -DskipTests
cp target/flags-demo.jar docker/flags-demo.jar
if [ ! -f docker/temurin27.tar.gz ]; then
curl -sL -o docker/temurin27.tar.gz \
"https://api.adoptium.net/v3/binary/latest/27/ga/linux/x64/jdk/hotspot/normal/eclipse"
fi
docker build -t flags-demo:jdk25 -f docker/Dockerfile.jdk25 docker
docker build -t flags-demo:jdk25-jdk -f docker/Dockerfile.jdk25-jdk docker
docker build -t flags-demo:jdk27 -f docker/Dockerfile.jdk27 docker
echo "== 01/02: GC selection matrix, JDK 25 vs JDK 27 (JEP 523) =="
combos=("256m:1" "512m:1" "1800m:1" "512m:2" "1800m:2" "4g:2")
for jdk in jdk25 jdk27; do
out="docs/output/0$([ "$jdk" = jdk25 ] && echo 1 || echo 2)-gc-selection-$jdk.txt"
: > "$out.tmp"
for c in "${combos[@]}"; do
mem="${c%%:*}"; cpu="${c##*:}"
echo "--- mem=$mem cpus=$cpu ---" >> "$out.tmp"
docker run --rm --memory="$mem" --cpus="$cpu" --entrypoint java "flags-demo:$jdk" \
-XX:+PrintFlagsFinal -version 2>/dev/null \
| grep -E "^\s*(bool UseG1GC|bool UseSerialGC|bool UseParallelGC|size_t MaxHeapSize|int ActiveProcessorCount|bool UseCompactObjectHeaders|double MaxRAMPercentage)\s*=" \
>> "$out.tmp"
echo "" >> "$out.tmp"
done
mv "$out.tmp" "$out"
done
echo "== 03: ActiveProcessorCount lying to the JVM =="
{
docker run --rm --cpus=1 --memory=1g --entrypoint java flags-demo:jdk27 \
-XX:+PrintFlagsFinal -version 2>/dev/null | grep -E "ParallelGCThreads|ConcGCThreads|ActiveProcessorCount"
echo ""
docker run --rm --cpus=1 --memory=1g --entrypoint java flags-demo:jdk27 \
-XX:ActiveProcessorCount=8 -XX:+PrintFlagsFinal -version 2>/dev/null | grep -E "ParallelGCThreads|ConcGCThreads|ActiveProcessorCount"
} > docs/output/03-activeprocessorcount-lie.txt.body
echo "== 04: compact object headers, byte-for-byte (JEP 534) =="
run_histo() {
local tag=$1 extra=$2 cname=$3
docker run -d --memory=1g --name "$cname" -v "$PWD/docker/headersize.jar:/headersize.jar" \
--entrypoint java "flags-demo:$tag" $extra -cp /headersize.jar HeaderSizeDemo 2000000 >/dev/null
sleep 6
docker exec "$cname" jcmd 1 GC.class_histogram 2>&1 | grep -E "Pair|Total"
docker rm -f "$cname" >/dev/null 2>&1
}
{
echo "--- JDK 25, default (UseCompactObjectHeaders=false) ---"
run_histo jdk25-jdk "" hdr25default
echo ""
echo "--- JDK 25, -XX:+UseCompactObjectHeaders ---"
run_histo jdk25-jdk "-XX:+UseCompactObjectHeaders" hdr25on
echo ""
echo "--- JDK 27, default (UseCompactObjectHeaders=true) ---"
run_histo jdk27 "" hdr27default
echo ""
echo "--- JDK 27, -XX:-UseCompactObjectHeaders ---"
run_histo jdk27 "-XX:-UseCompactObjectHeaders" hdr27off
} > docs/output/04-compact-object-headers.txt.body
echo "== 05: a correctly set MaxRAMPercentage fails safely =="
docker run --rm --memory=256m -v "$PWD/docker/memoryhog.jar:/memoryhog.jar" --entrypoint java flags-demo:jdk25 \
-Xlog:gc -XX:MaxRAMPercentage=75 -cp /memoryhog.jar MemoryHog 24 200 \
> docs/output/05-maxrampercentage-correct-oom.txt.body 2>&1 || true
echo "== 06: a hardcoded -Xmx bigger than the container gets the container killed =="
docker run --rm --memory=256m -v "$PWD/docker/memoryhog.jar:/memoryhog.jar" --entrypoint java flags-demo:jdk25 \
-Xlog:gc -Xmx1g -cp /memoryhog.jar MemoryHog 24 200 \
> docs/output/06-hardcoded-xmx-oomkilled.txt.body 2>&1
echo "(exit code above should be 137 - SIGKILL from the cgroup OOM killer)"
echo "== 07: -XX:-UseContainerSupport makes the JVM blind to the container =="
{
docker run --rm --memory=256m --entrypoint java flags-demo:jdk25 \
-XX:-UseContainerSupport -XX:+PrintFlagsFinal -version 2>/dev/null | grep -E "MaxHeapSize|UseContainerSupport"
echo ""
docker run --rm --memory=256m -v "$PWD/docker/memoryhog.jar:/memoryhog.jar" --entrypoint java flags-demo:jdk25 \
-Xlog:gc -XX:-UseContainerSupport -cp /memoryhog.jar MemoryHog 24 200
} > docs/output/07-usecontainersupport-false-oomkilled.txt.body 2>&1 || true
echo "== 08: -XX:+HeapDumpOnOutOfMemoryError actually leaves a dump =="
mkdir -p /tmp/flags-demo-dumps
docker run --rm --memory=256m -v "$PWD/docker/memoryhog.jar:/memoryhog.jar" -v /tmp/flags-demo-dumps:/dumps \
--entrypoint java flags-demo:jdk25-jdk \
-XX:MaxRAMPercentage=75 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps/heap.hprof \
-cp /memoryhog.jar MemoryHog 24 200 > docs/output/08-heapdumponoutofmemoryerror.txt.body 2>&1 || true
ls -la /tmp/flags-demo-dumps >> docs/output/08-heapdumponoutofmemoryerror.txt.body
rm -rf /tmp/flags-demo-dumps
echo "== 09: -XX:NativeMemoryTracking=summary =="
docker run -d --memory=512m --name nmtdemo -v "$PWD/docker/flags-demo.jar:/app.jar" --entrypoint java flags-demo:jdk25-jdk \
-XX:NativeMemoryTracking=summary -XX:MaxRAMPercentage=75 -jar /app.jar >/dev/null
sleep 6
docker exec nmtdemo jcmd 1 VM.native_memory summary > docs/output/09-native-memory-tracking.txt.body 2>&1
docker rm -f nmtdemo >/dev/null 2>&1
echo "Done. Each docs/output/NN-*.txt.body is the fresh capture; prepend the command"
echo "comment block from the committed .txt file by hand if you are regenerating for a diff."