#!/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."