Java -version output
openjdk version "25.0.4.1" 2026-08-18 LTS
IBM Semeru Runtime Open Edition 25.0.4.15 (build 25.0.4.1+1-LTS)
Eclipse OpenJ9 VM 25.0.4.15 (build 25.0.4.1+1-openj9-0.61.0, JRE 25 Mac OS X aarch64-64-Bit 20260818_330 (JIT enabled, AOT enabled)
OpenJ9 - a1daf225f5
OMR - ebd02d912
JCL - cdc2304e39f based on jdk-25.0.4.1+1)
macOS 15, aarch64
Summary of problem
A single-threaded, fully deterministic program (fixed seed, no I/O, no clocks, no hashing in the output path) gives different outputs across runs of the same binary on the same machine when the OpenJ9 JIT is enabled with default options. The reproducer runs the same fixed-seed accumulation logic twice inside one JVM (R1, R2) and prints DUP=same|diff. Under the default sampled compilation strategy, R1 intermittently takes a fresh wrong value on every affected run, while R2 is always correct — 160/160 observed runs across four builds. This localizes the defect to the transition window during the first execution of the loop nest (interpreted → compiled): once the method is fully compiled, results are correct again.
Core shape of the affected loop (JLS 5.1.4 / 15.17.2 int wrap-around semantics):
Random r = new Random(42);
for (int i = 0; i < 500000; i++) {
x += Math.abs(r.nextInt());
while (x < 0) x += 1000000000; // repair int overflow wrap-around
agg += (x % 97);
}
R1 must equal R2 and must equal the interpreter result (23997686) on every run of every conforming JVM.
Trigger characterization (all on default options unless noted):
- Structure: the int-overflow wrap-around correction loop is the trigger surface. An otherwise identical program with the correction replaced by manual bit arithmetic is 0/63 runs nondeterministic (negative control). The aggregation style and the RNG are not involved: a hand-written LCG variant still misbehaves (5–20% of runs), ruling out
java.util.Random variance.
- High-rate form: polynomial aggregation (
agg = agg*31 + (x%97)) with a while correction raises the trigger rate to 50–75% per run; wrong values are amplified by the polynomial to sign-flipped magnitudes.
- Scale: 500,000 iterations triggers; 250,000 and below → 0/20 on both 17.0.20 and 25.0.4 (the JIT must take over mid-loop for the window to open).
- Compilation mode: default sampled escalation triggers; explicit
-Xjit:optlevel=cold / -Xjit:optlevel=hot and -Xint are always correct.
Affected version matrix (macOS 15, aarch64):
- Affected (default options): Semeru 11.0.32, 17.0.20, 21.0.12, 25.0.4 — all four releases intermittently nondeterministic (5–40% of runs for the base shape).
- Unaffected (controls, same binaries):
-Xint; -Xjit:optlevel=cold; -Xjit:optlevel=hot; Temurin 17/21/25 (default and -Xint).
Violated specification: a deterministic Java program (fixed seed, single thread, pure integer arithmetic with JLS-mandated wrap-around) must produce the same result on every execution of the same JVM build. JIT-compiled code is required to be observationally consistent with interpreted code of the same JVM; here the same method produces different results across runs, and even within a single run (DUP=diff, R1 wrong / R2 right).
This issue was identified via differential testing across multiple JVM implementations (Temurin and IBM Semeru/OpenJ9, JDK 11–25).
Steps to reproduce
javac repro.java
java repro — repeat ~10–20 times; intermittently prints DUP=diff with a fresh wrong R1 each time (probability 5–40% per run depending on build; the polynomial variant reaches 50–75%)
Expected result
CHAINL=5
R1=23997686
R2=23997686
DUP=same
on every run, in every execution mode, on every conforming JVM.
Actual result
Example (Semeru 17.0.20, default, 20-run sample — 8 wrong, 8 distinct values; all exit code 0, no exceptions):
run=1 R1=24012322 R2=23997686 DUP=diff
run=2 R1=24000473 R2=23997686 DUP=diff
run=5 R1=23996673 R2=23997686 DUP=diff
run=8 R1=23996515 R2=23997686 DUP=diff
run=11 R1=24005587 R2=23997686 DUP=diff
run=13 R1=23993056 R2=23997686 DUP=diff
run=19 R1=24002947 R2=23997686 DUP=diff
run=20 R1=24002933 R2=23997686 DUP=diff
Reproduction matrix (2 rounds × 20 runs × 4 builds, default options; DUP=diff counts):
| Semeru build |
round 1 |
round 2 |
| 11.0.32 |
7/20 |
6/20 |
| 17.0.20 |
8/20 |
3/20 |
| 21.0.12 |
4/20 |
5/20 |
| 25.0.4 |
4/20 |
1/20 |
Controls (-Xint, -Xjit:optlevel=cold, -Xjit:optlevel=hot, Temurin 17/21/25): 0 wrong in every sample. R2 correct in 160/160 runs across all affected builds.
Source code / Diagnostic files
import java.util.Random;
public class repro {
static long runOnce() {
Random r = new Random(42);
int x = 0;
long agg = 0;
for (int i = 0; i < 500000; i++) {
x += Math.abs(r.nextInt());
while (x < 0) {
x += 1000000000; // correct int wrap-around
}
agg += (x % 97);
}
return agg;
}
static void realMain() {
System.out.println("CHAINL=5");
long r1 = runOnce();
long r2 = runOnce();
System.out.println("R1=" + r1);
System.out.println("R2=" + r2);
System.out.println("DUP=" + (r1 == r2 ? "same" : "diff"));
}
public static void main(String[] args) {
try {
realMain();
} catch (Throwable t) {
System.err.println(t.getClass().getName() + ": " + t.getMessage());
System.exit(1);
}
}
}
Supporting variants used for the characterization above (happy to attach if useful): LCG-isolated form (rules out java.util.Random), polynomial high-rate form (50–75% trigger), and the bit-arithmetic negative control (0/63).
Java -version output
macOS 15, aarch64
Summary of problem
A single-threaded, fully deterministic program (fixed seed, no I/O, no clocks, no hashing in the output path) gives different outputs across runs of the same binary on the same machine when the OpenJ9 JIT is enabled with default options. The reproducer runs the same fixed-seed accumulation logic twice inside one JVM (
R1,R2) and printsDUP=same|diff. Under the default sampled compilation strategy,R1intermittently takes a fresh wrong value on every affected run, whileR2is always correct — 160/160 observed runs across four builds. This localizes the defect to the transition window during the first execution of the loop nest (interpreted → compiled): once the method is fully compiled, results are correct again.Core shape of the affected loop (JLS 5.1.4 / 15.17.2 int wrap-around semantics):
R1must equalR2and must equal the interpreter result (23997686) on every run of every conforming JVM.Trigger characterization (all on default options unless noted):
java.util.Randomvariance.agg = agg*31 + (x%97)) with awhilecorrection raises the trigger rate to 50–75% per run; wrong values are amplified by the polynomial to sign-flipped magnitudes.-Xjit:optlevel=cold/-Xjit:optlevel=hotand-Xintare always correct.Affected version matrix (macOS 15, aarch64):
-Xint;-Xjit:optlevel=cold;-Xjit:optlevel=hot; Temurin 17/21/25 (default and-Xint).Violated specification: a deterministic Java program (fixed seed, single thread, pure integer arithmetic with JLS-mandated wrap-around) must produce the same result on every execution of the same JVM build. JIT-compiled code is required to be observationally consistent with interpreted code of the same JVM; here the same method produces different results across runs, and even within a single run (
DUP=diff,R1wrong /R2right).This issue was identified via differential testing across multiple JVM implementations (Temurin and IBM Semeru/OpenJ9, JDK 11–25).
Steps to reproduce
javac repro.javajava repro— repeat ~10–20 times; intermittently printsDUP=diffwith a fresh wrongR1each time (probability 5–40% per run depending on build; the polynomial variant reaches 50–75%)Expected result
on every run, in every execution mode, on every conforming JVM.
Actual result
Example (Semeru 17.0.20, default, 20-run sample — 8 wrong, 8 distinct values; all exit code 0, no exceptions):
Reproduction matrix (2 rounds × 20 runs × 4 builds, default options;
DUP=diffcounts):Controls (
-Xint,-Xjit:optlevel=cold,-Xjit:optlevel=hot, Temurin 17/21/25): 0 wrong in every sample.R2correct in 160/160 runs across all affected builds.Source code / Diagnostic files
Supporting variants used for the characterization above (happy to attach if useful): LCG-isolated form (rules out
java.util.Random), polynomial high-rate form (50–75% trigger), and the bit-arithmetic negative control (0/63).