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
57 lines
3.1 KiB
Bash
Executable File
57 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Heap footprint of the same jar on JDK 25, 26 and 27, with compact object headers on and off.
|
|
# usage: footprint.sh [N products] [runs per configuration]
|
|
source "$(dirname "$0")/lib.sh"
|
|
N="${1:-2000000}"; RUNS="${2:-3}"
|
|
TMP="$(mktemp -d)"
|
|
MEDS=(); ALL=()
|
|
|
|
row() { # label jdk flags...
|
|
local label="$1" jh="$2"; shift 2
|
|
local idle=() live=() rss=() gcn=() gcms=() ns=() start=()
|
|
for r in $(seq 1 "$RUNS"); do
|
|
run_app "$jh" "$N" "$TMP/$label-$r" -Xms1g -Xmx1g "$@" || { echo "$label run $r FAILED"; return; }
|
|
local d="$TMP/$label-$r"
|
|
idle+=("$(kv "$d/memory-idle.txt" heapLiveAfterGcMB)")
|
|
live+=("$(kv "$d/memory.txt" heapLiveAfterGcMB)")
|
|
rss+=("$(kv "$d/memory.txt" rssMB)")
|
|
gcn+=("$(kv "$d/load.txt" gcCountDuringLoad)")
|
|
gcms+=("$(kv "$d/load.txt" gcMillisDuringLoad)")
|
|
ns+=("$(kv "$d/lookups.txt" nsPerLookup)")
|
|
ALL+=("${gcn[-1]} ${rss[-1]} ${ns[-1]}")
|
|
printf 'run %s/%s idle=%sMB live=%sMB rss=%sMB gcDuringLoad=%s (%sms) lookup=%sns compact=%s\n' "$label" "$r" \
|
|
"${idle[-1]}" "${live[-1]}" "${rss[-1]}" "${gcn[-1]}" "${gcms[-1]}" "${ns[-1]}" "$(kv "$d/memory.txt" UseCompactObjectHeaders)"
|
|
done
|
|
MEDS+=("$(printf 'MED %-22s idle=%sMB live=%sMB rss=%sMB gcDuringLoad=%s gcMs=%s lookupNs=%s' "$label" \
|
|
"$(printf '%s\n' "${idle[@]}" | median)" "$(printf '%s\n' "${live[@]}" | median)" "$(printf '%s\n' "${rss[@]}" | median)" \
|
|
"$(printf '%s\n' "${gcn[@]}" | median)" "$(printf '%s\n' "${gcms[@]}" | median)" "$(printf '%s\n' "${ns[@]}" | median)")")
|
|
echo "${MEDS[-1]}"
|
|
}
|
|
|
|
echo "# $(date -u +%F) ${N} products, -Xms1g -Xmx1g, G1, ${RUNS} runs per configuration; MED lines are medians"
|
|
echo "# JDK25=$("$JDK25/bin/java" -version 2>&1 | sed -n 2p)"
|
|
echo "# JDK26=$("$JDK26/bin/java" -version 2>&1 | sed -n 2p)"
|
|
echo "# JDK27=$("$JDK27/bin/java" -version 2>&1 | sed -n 2p)"
|
|
row jdk25-default "$JDK25"
|
|
row jdk25-compact "$JDK25" -XX:+UseCompactObjectHeaders
|
|
row jdk26-default "$JDK26"
|
|
row jdk26-compact "$JDK26" -XX:+UseCompactObjectHeaders
|
|
row jdk27-default "$JDK27"
|
|
row jdk27-optout "$JDK27" -XX:-UseCompactObjectHeaders
|
|
echo
|
|
echo "== medians of $RUNS runs per configuration (idle = live set of the idle app; live = after loading the catalog)"
|
|
printf '%s\n' "${MEDS[@]}"
|
|
echo
|
|
stats() { sort -n | awk '{a[NR]=$1} END{printf "min %d / median %d / max %d", a[1], a[int((NR+1)/2)], a[NR]}'; }
|
|
echo "== the same runs, grouped by whether a collection ran while the catalog was loading (all configurations together)"
|
|
for g in zero some; do
|
|
if [ $g = zero ]; then sel='$1==0'; label="no collection during load"; else sel='$1>0'; label="a collection during load"; fi
|
|
n=$(printf '%s\n' "${ALL[@]}" | awk "$sel" | wc -l)
|
|
printf '%-27s runs=%-3s rss MB: %s lookup ns: %s\n' "$label" "$n" \
|
|
"$(printf '%s\n' "${ALL[@]}" | awk "$sel {print \$2}" | stats)" "$(printf '%s\n' "${ALL[@]}" | awk "$sel {print \$3}" | stats)"
|
|
done
|
|
# keep one histogram per JVM for the class-level table
|
|
mkdir -p "$MOD/target/hist"
|
|
for c in jdk26-default jdk27-default jdk27-optout; do cp "$TMP/$c-1/histogram.txt" "$MOD/target/hist/$c.txt"; done
|
|
rm -rf "$TMP"
|