oracle/graal · error · PermanentBailoutException

Unbalanced monitor enter-exit in OSR compilation with locks:

Error message

Unbalanced monitor enter-exit in OSR compilation with locks: 

What it means

PermanentBailoutException thrown when VerifyLockDepthPhase, run after OnStackReplacementPhase re-inserts monitor enters for the OSR entry, reports a LockStructureError. The error message is prefixed with 'Unbalanced monitor enter-exit in OSR compilation with locks: ' and describes the exact lock-depth violation. It means the reconstructed lock ordering at the OSR entry would not match the interpreter's monitor stack, so compiled code could not legally acquire/release the monitors.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/phases/OnStackReplacementPhase.java:237

            OsrWithLocksCount.increment(debug);
            try (DebugCloseable context = osrStart.withNodeSourcePosition()) {
                for (int i = osrState.monitorIdCount() - 1; i >= 0; --i) {
                    MonitorIdNode id = osrState.monitorIdAt(i);
                    ValueNode lockedObject = osrState.lockAt(i);
                    OSRMonitorEnterNode osrMonitorEnter = graph.add(new OSRMonitorEnterNode(lockedObject, id));
                    osrMonitorEnter.setStateAfter(osrStart.stateAfter());
                    FixedNode oldNext = osrStart.next();
                    oldNext.replaceAtPredecessor(null);
                    osrMonitorEnter.setNext(oldNext);
                    osrStart.setNext(osrMonitorEnter);
                }
            }

            debug.dump(DebugContext.DETAILED_LEVEL, graph, "After inserting OSR monitor enters");
            try {
                new VerifyLockDepthPhase().run(graph);
            } catch (VerifyLockDepthPhase.LockStructureError e) {
                throw new PermanentBailoutException("Unbalanced monitor enter-exit in OSR compilation with locks: " + e.getMessage());
            }
        }
        debug.dump(DebugContext.DETAILED_LEVEL, graph, "OnStackReplacement result");
        new DeadCodeEliminationPhase(Required).apply(graph);
        /*
         * There must not be any parameter nodes left after OSR compilation.
         */
        assert graph.getNodes(ParameterNode.TYPE).count() == 0 : "OSR Compilation contains references to parameters.";
    }

    /**
     * Generates a speculative type check on {@code osrLocal} for {@code narrowedStamp}.
     *
     * @return a {@link PiNode} that narrows the type of {@code osrLocal} to {@code narrowedStamp}
     */
    private static ValueNode narrowOsrLocal(StructuredGraph graph, Stamp narrowedStamp, ValueNode osrLocal, SpeculationReason reason,
                    OSRStartNode osrStart, EntryProxyNode proxy, FrameState osrState) {

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. No JVM action needed: the bailout is safe; HotSpot continues interpreting the method.
  2. Simplify the synchronization structure around the hot loop (hoist synchronized out of the loop) to make OSR+locks verifiable.
  3. Compiler developers: reproduce with -Dgraal.Dump=:2 and inspect 'After inserting OSR monitor enters' to see which path breaks lock-depth balance.
Defensive patterns

Strategy: fallback

Try / catch

try {
    compileResult = compile(task);
} catch (PermanentBailoutException e) {
    if (e.getMessage().startsWith("Unbalanced monitor enter-exit")) {
        // safe fallback: interpreter continues; log and move on
    }
}

Prevention

When it happens

Trigger: OSR compilation of a method with monitors where re-materializing monitorenter nodes at OSR start yields a lock depth that does not return to zero or mismatches per exception path — typically nested/conditional synchronization on the OSR path with SupportOSRWithLocks enabled.

Common situations: Complex synchronized control flow (locks acquired conditionally inside the loop, nested monitors, try/finally around monitorexit) hitting OSR; usually not actionable by end users — it is a compiler limitation that safely falls back to the interpreter.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/586708bbe7d194dd. Report an issue: GitHub.