XPerf
LanguagesJava

jvmperf evidence policy

Gate

A JP rule may be documented / treated as error (keep / escalate contract for guides and future --preset) only if both exist under packages/jvmperf/evidence/:

  1. <RULE>.md — claim, harness, JDK version + JVM flags (include -XX:+UseCompactObjectHeaders when used), limits
  2. <RULE>.jsonl — ≥1 before/after measurement pair with fields such as rule, case, role (before = smell, after = fix), jdk, jvm_args, ns_per_op (or median_ms / total_size_bytes), alloc_b_per_op, ratio, ts

Otherwise the rule stays hint or off (a review prompt): fine for dogfood and fixtures, not a “must fix for speed” signal.

CLI may still exit non-zero on any finding (unless --no-fail); printed severity labels follow this policy table. Full --preset wiring is optional later (mirror jsperf/cperf).

Correlation siblings

  • Wall-clock / B/op agreement: scripts/run-static-vs-hotspot.sh (writes research/benches/static-vs-hotspot.{json,jsonl})

Do not escalate because PrintCompilation / compilation-log line counts look scary — those are noisy (same lesson as V8 --trace-deopt counts).

Current map

As of 2026-10-22 (Asia/Shanghai); benches on JDK 25.0.4.1 + COH. Policy is each rule's default severity in Rule.java.

RulePolicyEvidenceCorrelate / note
jp-boxed-primitive-collectionoffjp-boxed-primitive-collectionJDK 27 n=1e6 ~1.32×, n=1e5 ~7.37×; quiet JDK 21 ~11× / ~8.5×
jp-string-concat-hoterrorjp-string-concat-hotJDK 27 J23 parts=256 ~21.3×
jp-regex-hotoffjp-regex-hotJDK 27 time ~0.76×, alloc ~2.4×; quiet JDK 21 time ~1.67×
jp-format-hotoffjp-format-hotJDK 27 ~29×
jp-unpresized-collectionoffjp-unpresized-collectionJDK 27 n=10k ~1.43×
jp-boxed-streamerrorjp-boxed-streamJDK 27 boxedReduce/mapToInt ~37×
jp-exception-control-flowoffjp-exception-control-flowJDK 27 ~4.0× (spikes); quiet JDK 21 ~27×
jp-legacy-synchronized-typeremovedtombstonemeasured ~1.08× / HT negative — matcher deleted
jp-many-boolean-fieldsremovedtombstoneCOH 16=16 no win — matcher deleted
jp-enum-key-hash-collectionerrorjp-enum-key-hash-collectionJDK 27 EnumMap ~11.6×
jp-collection-chainoffjp-collection-chainJDK 27 / Kotlin 2.4.21 eager/seq ~265×
jp-value-class-boxinghintjp-value-class-boxingJDK 27 time ~4.0×, alloc ~5.0×
jp-lambda-to-non-inline-hotoffjp-lambda-to-non-inline-hotJDK 27 alloc ~244×; time ~0.47×
jp-lazy-default-modeoffjp-lazy-default-mode.md onlypending measure — situational lazy mode
jp-data-copy-hoterrorjp-data-copy-hotJDK 27 time ~208×, alloc ~10001×
jp-android-sparse-arrayhintstub mdpending — needs ART/device
jp-log-hotoffjp-log-hotJDK 27 time mean ~0.43× (one spike); quiet JDK 21 ~18×; alloc ~23200 B
jp-sleep-pollingoffstub mdpending — sleep dominates; design not micro
jp-finalizehintstub mdpending — finalize correctness / Cleaner
jp-expensive-alloc-hotoffjp-expensive-alloc-hottiny-pool negative
jp-hot-heavy-allocoffstub mdpending — ART/device hot alloc
jp-hot-format-logoffstub mdpending — ART/device hot format/log
jp-hot-system-callhintstub mdpending — ART/device system call
jp-bitmap-decode-no-reusehintstub mdpending — ART/device Bitmap
jp-hot-post-lambdahintstub mdpending — ART Handler post
jp-ui-text-appendhintstub mdpending — ART UI text
jp-baos-tobytearray-copyerrorjp-baos-tobytearray-copyJDK 27 toByteArray/writeTo ~99×
jp-hot-payload-copyerrorjp-hot-payload-copyJDK 27 newFrame/reuse ~158×
vt-unsafe-threadlocal-pool— (not shipped)stub; guide fixturesguide-first §10.2
jp-client-factory-recreateerrorjp-client-factory-recreateJDK 27 HttpClient new+close/reuse ~1.97e5×
jp-string-intern-hoterrorjp-string-intern-hotJDK 27 intern/plain ~6.3×
jp-non-static-field-updaterhintjp-non-static-field-updaterJDK 27 create ~526×; quiet JDK 21 steady-state ~1.13×

Counts (current defaults): active rules 29 (was 31; removed jp-legacy-synchronized-type / jp-many-boolean-fields): 8 error, 8 hint, 13 off. The table still lists the tombstones and the not-shipped rule. Default severity follows Rule.java / G3 reverse-opt.

Regenerating

cd packages/jvmperf
# Lint/tests: JAVA_HOME → JDK 21. Optional benches may use 25:
JAVA_HOME=/usr/lib/jvm/java-25-openjdk-amd64 bash scripts/run_jmh.sh J32_RuleEvidenceBatch
JAVA_HOME=/usr/lib/jvm/java-25-openjdk-amd64 bash scripts/run_kotlin_alloc.sh K15
# then refresh evidence/*.jsonl from bench-results if numbers move

Re-run after JDK major upgrades. Never invent ratios.

Also see shared rule-regression.md.