Add jdk27-memory: compact object headers and G1-everywhere on a Spring Boot service

Spring Boot 4.1.1 catalog service measured on JDK 25, 26 and 27: live heap 320 MB to 273 MB with compact headers, per-class bytes per instance, container-sizing sweeps with real cgroup limits, 1-CPU Serial vs G1 with Native Memory Tracking, removed flags (MaxRAM, UseCompressedClassPointers), and a kernel OOM-kill check. Outputs in jdk27-memory/output.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01TF9JWFvJSNm6HVzswzZU5a
This commit is contained in:
Claude
2026-09-24 15:09:55 +00:00
parent e189da79ba
commit e83c9949d8
29 changed files with 1041 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# The smallest container that can hold the catalog, before and after. For each memory limit the app is started
# inside a cgroup with that limit and asked to load N products; the table says what happened.
# ok the load finished
# java-OOM the JVM threw OutOfMemoryError: the heap (a fraction of the limit) was too small
# killed(...) the kernel killed the process: total memory, not just heap, went over the limit
# no-result-in-25s the JVM stayed alive but the load (about a second when healthy) did not finish in 25
# seconds: the heap is so tight that the collector runs continuously and makes no progress
# Each cell is RUNS runs ("3/3 ok" = all three finished); a cell near the boundary can be mixed.
# usage: container-sizing.sh [N products] [runs per cell]
source "$(dirname "$0")/lib.sh"
N="${1:-2000000}"; RUNS="${2:-3}"
TMP="$(mktemp -d)"; RETRIES=0
MB=$((1024*1024))
sweep() { # title, jvm-flags-for-all, limits...
local title="$1" common="$2"; shift 2
echo "== $title"
printf '%-8s %-8s %-30s %-30s %-30s\n' "limit" "heap" "JDK 26 default" "JDK 27 default" "JDK 27 opt-out"
for lim in "$@"; do
local cells=() heap="?"
for cfg in "26|$JDK26|" "27|$JDK27|" "27o|$JDK27|-XX:-UseCompactObjectHeaders"; do
IFS='|' read -r tag jh fl <<<"$cfg"
local ok=0 fails=() rss=()
for r in $(seq 1 "$RUNS"); do
d="$TMP/$tag-$lim-$r"
CG_MEM=$((lim*MB)) CG_CPUS=2 run_app "$jh" "$N" "$d" $common $fl >/dev/null 2>&1
h=$(kv "$d/memory-idle.txt" maxHeapMB); [ -n "$h" ] && heap="$h"
[ -f "$d/retries" ] && RETRIES=$((RETRIES+1))
out=$(cat "$d/outcome.txt")
if [ "$out" = ok ]; then ok=$((ok+1)); rss+=("$(kv "$d/memory.txt" rssMB)"); else fails+=("${out%%(*}"); fi
done
cell="$ok/$RUNS ok"
if [ $ok -gt 0 ]; then cell="$cell rss $(printf '%s\n' "${rss[@]}" | median)MB"; fi
if [ ${#fails[@]} -gt 0 ]; then cell="$cell [$(printf '%s\n' "${fails[@]}" | sort | uniq -c | awk '{printf "%s%sx %s",sep,$1,$2; sep=" "}')]"; fi
cells+=("$cell")
done
printf '%-8s %-8s %-30s %-30s %-30s\n' "${lim}MB" "${heap}MB" "${cells[0]}" "${cells[1]}" "${cells[2]}"
done
echo
}
# ONLY=75 or ONLY=25 runs one of the two sweeps (each takes about 12 minutes); default is both.
ONLY="${ONLY:-both}"
echo "# $N products; 2 CPUs; $RUNS runs per cell; rss is the median of the runs that finished"
if [ "$ONLY" != 25 ]; then
sweep "Heap set to 75% of the container (-XX:MaxRAMPercentage=75)" "-XX:MaxRAMPercentage=75" 400 416 432 448 512 704
fi
if [ "$ONLY" != 75 ]; then
sweep "Heap left at the JVM default (25% of the container)" "" 1024 1152 1216 1280 1536 2048
fi
echo "# runs that needed a startup retry (the JVM never came up, for a reason other than OOM or a kernel kill): $RETRIES"
rm -rf "$TMP"