vt-pinning: diagnosing virtual thread pinning companion code

Reproduces the pinning JEP 491 did not fix (native JNI frames) with a real
native library, alongside the monitor case it did fix. Covers the
jdk.VirtualThreadPinned JFR event, reading pinned vs unmounted virtual
threads from a jcmd Thread.dump_to_file, the measured throughput cost on a
capped carrier pool, and a bounded-executor pattern to contain the blast
radius.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01FhzLY5p6okFva3qsnsRyvM
This commit is contained in:
2026-09-30 06:35:35 +00:00
co-authored by Claude Sonnet 5
parent ff183da163
commit 0819993c51
18 changed files with 725 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# Regenerates every file in output/. Requires:
# - JDK25_HOME pointing at a JDK 24+ install (this repo used Temurin 25.0.4.1+1)
# - JDK21_HOME pointing at a pre-JDK-24 install (this repo used OpenJDK 21.0.10), for the
# before/after comparisons against JEP 491
# - gcc on PATH (builds the JNI native library)
set -euo pipefail
cd "$(dirname "$0")/.."
: "${JDK25_HOME:?Set JDK25_HOME to a JDK 24+ install}"
: "${JDK21_HOME:?Set JDK21_HOME to a pre-JDK-24 install}"
JAVA_HOME="$JDK21_HOME" ./scripts/build-native.sh
mkdir -p target/classes target/classes-21
"$JDK25_HOME/bin/javac" --release 25 -d target/classes $(find src/main/java -name "*.java")
"$JDK21_HOME/bin/javac" --release 21 -d target/classes-21 $(find src/main/java -name "*.java")
NATLIB=target/native
echo "--- 01: monitor pinning, JDK21 vs JDK25 (JEP 491) ---"
{
echo '$ java -Djdk.tracePinnedThreads=full MonitorPinningDemo (pre-JDK-24 runtime)'
echo ""
"$JDK21_HOME/bin/java" -Djdk.tracePinnedThreads=full -cp target/classes-21 \
com.ankurm.vtpinning.MonitorPinningDemo 2>&1 | grep -v "Picked up"
echo ""
echo '$ java -Djdk.tracePinnedThreads=full MonitorPinningDemo (JDK 24+ runtime)'
echo ""
"$JDK25_HOME/bin/java" -Djdk.tracePinnedThreads=full -cp target/classes \
com.ankurm.vtpinning.MonitorPinningDemo 2>&1 | grep -v "Picked up"
} > output/01-monitor-pinning-jdk21-vs-jdk25.txt
echo "--- 02: native pinning, JDK21 vs JDK25 (still real on both) ---"
{
echo '$ java -Djdk.tracePinnedThreads=full NativePinningDemo (pre-JDK-24 runtime)'
echo ""
"$JDK21_HOME/bin/java" -Djdk.tracePinnedThreads=full -Djava.library.path=$NATLIB -cp target/classes-21 \
com.ankurm.vtpinning.NativePinningDemo 2>&1 | grep -v "Picked up"
echo ""
echo '$ java -Djdk.tracePinnedThreads=full NativePinningDemo (JDK 24+ runtime - flag is inert)'
echo ""
"$JDK25_HOME/bin/java" --enable-native-access=ALL-UNNAMED -Djdk.tracePinnedThreads=full -Djava.library.path=$NATLIB -cp target/classes \
com.ankurm.vtpinning.NativePinningDemo 2>&1 | grep -v "Picked up\|WARNING"
} > output/02-native-pinning-jdk21-vs-jdk25.txt
echo "--- 03: jdk.VirtualThreadPinned JFR event on JDK25 ---"
rm -f target/native-pin.jfr
{
echo '$ java -XX:StartFlightRecording=...,jdk.VirtualThreadPinned#enabled=true,threshold=0ms NativePinningDemo'
echo ""
"$JDK25_HOME/bin/java" --enable-native-access=ALL-UNNAMED \
"-XX:StartFlightRecording=filename=target/native-pin.jfr,settings=profile,jdk.VirtualThreadPinned#enabled=true,jdk.VirtualThreadPinned#threshold=0ms,jdk.VirtualThreadPinned#stackTrace=true" \
-Djava.library.path=$NATLIB -cp target/classes com.ankurm.vtpinning.NativePinningDemo 2>&1 \
| grep -v "Picked up\|WARNING\|jfr,startup\|^\["
echo ""
echo '$ jfr print --events jdk.VirtualThreadPinned --stack-depth 10 target/native-pin.jfr'
echo ""
"$JDK25_HOME/bin/jfr" print --events jdk.VirtualThreadPinned --stack-depth 10 target/native-pin.jfr 2>&1 | grep -v "Picked up"
} > output/03-jfr-virtualthreadpinned-event.txt
echo "--- 04: jcmd Thread.dump_to_file, pinned vs unpinned virtual thread ---"
rm -f target/threaddump.json target/threaddump_out.txt
"$JDK25_HOME/bin/java" --enable-native-access=ALL-UNNAMED -Djava.library.path=$NATLIB -cp target/classes \
com.ankurm.vtpinning.ThreadDumpPinnedDemo > target/threaddump_out.txt 2>&1 &
PID=$!
sleep 1.5
"$JDK25_HOME/bin/jcmd" "$PID" Thread.dump_to_file -format=json target/threaddump.json > target/jcmd_out.txt 2>&1
wait $PID
{
echo '$ java ThreadDumpPinnedDemo &'
grep -v "Picked up" target/threaddump_out.txt
echo ""
echo '$ jcmd <pid> Thread.dump_to_file -format=json threaddump.json (taken mid-flight)'
grep -v "Picked up" target/jcmd_out.txt
echo ""
echo '--- the two virtual thread entries from that dump ---'
python3 - <<'PYEOF'
import json
with open('target/threaddump.json') as f:
data = json.load(f)
for c in data['threadDump']['threadContainers']:
for t in c.get('threads', []):
if t.get('virtual'):
print(json.dumps(t, indent=2))
print()
PYEOF
} > output/04-jcmd-thread-dump-pinned-vs-unpinned.txt
echo "--- 05: throughput cost of native pinning vs plain blocking (capped carriers) ---"
{
echo '$ java -Djdk.virtualThreadScheduler.parallelism=2 PinningThroughputDemo native 4 1000'
"$JDK25_HOME/bin/java" --enable-native-access=ALL-UNNAMED -Djdk.virtualThreadScheduler.parallelism=2 -Djava.library.path=$NATLIB -cp target/classes \
com.ankurm.vtpinning.PinningThroughputDemo native 4 1000 2>&1 | grep -v "Picked up\|WARNING"
echo ""
echo '$ java -Djdk.virtualThreadScheduler.parallelism=2 PinningThroughputDemo plain 4 1000'
"$JDK25_HOME/bin/java" -Djdk.virtualThreadScheduler.parallelism=2 -Djava.library.path=$NATLIB -cp target/classes \
com.ankurm.vtpinning.PinningThroughputDemo plain 4 1000 2>&1 | grep -v "Picked up\|WARNING"
} > output/05-pinning-throughput-cost.txt
echo "--- 06: bounding the blast radius with a dedicated executor ---"
{
echo '$ java -Djdk.virtualThreadScheduler.parallelism=2 BoundedNativeCallDemo direct'
"$JDK25_HOME/bin/java" --enable-native-access=ALL-UNNAMED -Djdk.virtualThreadScheduler.parallelism=2 -Djava.library.path=$NATLIB -cp target/classes \
com.ankurm.vtpinning.BoundedNativeCallDemo direct 2>&1 | grep -v "Picked up\|WARNING"
echo ""
echo '$ java -Djdk.virtualThreadScheduler.parallelism=2 BoundedNativeCallDemo bounded'
"$JDK25_HOME/bin/java" --enable-native-access=ALL-UNNAMED -Djdk.virtualThreadScheduler.parallelism=2 -Djava.library.path=$NATLIB -cp target/classes \
com.ankurm.vtpinning.BoundedNativeCallDemo bounded 2>&1 | grep -v "Picked up\|WARNING"
} > output/06-bounded-executor-fix.txt
echo "Done. See output/."