oracle/graal · warning · PermanentBailoutException

Too many loops in method

Error message

Too many loops in method

What it means

Thrown by BciBlockMapping.makeLoopHeader when the number of distinct loops in a method exceeds LOOP_HEADER_MAX_CAPACITY. The loop-header bookkeeping (BitSet per block, loopHeaders array) is sized for a bounded number of loops; exceeding the cap is treated as an artificial sanity limit and permanently bails out compilation rather than feeding the compiler an unreasonable loop structure.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BciBlockMapping.java:1729

     * The next available loop number.
     */
    private int nextLoop;

    /**
     * Returns the smallest power of 2, strictly greater than value.
     */
    private static int nextPowerOfTwo(int value) {
        assert NumUtil.assertNonNegativeInt(value);
        return 1 << (32 - Integer.numberOfLeadingZeros(value));
    }

    private void makeLoopHeader(BciBlock block) {
        assert !block.isLoopHeader;
        block.isLoopHeader = true;
        if (nextLoop >= LOOP_HEADER_MAX_CAPACITY) {
            // This is an artificial restriction, a sanity check to avoid feeding the compiler an
            // unreasonable number of loops.
            throw new PermanentBailoutException("Too many loops in method");
        }
        block.loops.set(nextLoop);
        debug.log("makeLoopHeader(%s) -> %s", block, block.loops);
        if (loopHeaders == null) {
            loopHeaders = new BciBlock[Math.max(nextPowerOfTwo(nextLoop), LOOP_HEADER_INITIAL_CAPACITY)];
        } else if (nextLoop >= loopHeaders.length) {
            int newLength = nextPowerOfTwo(nextLoop);
            loopHeaders = Arrays.copyOf(loopHeaders, newLength);
        }
        loopHeaders[nextLoop] = block;
        block.loopId = nextLoop;
        nextLoop++;
    }

    private void propagateLoopBits(TraversalStep step, BitSet loopBits) {
        TraversalStep s = step;
        while (s != null) {
            // Original condition: if (s.block.loops & loopBits == loopBits) break;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Reduce the number of loops in the offending method (split the method, simplify generated code, or disable extreme loop generation in the generator).
  2. Regenerate the class with the loops batched into inner helper methods.
  3. Let HotSpot fall back to another tier for this method — the bailout is permanent for Graal by design.
Defensive patterns

Strategy: fallback

Try / catch

// Permanent bailout: HotSpot falls back to another compiler for this method.
// Nothing to catch; reduce loop count in the source/generated bytecode.

Prevention

When it happens

Trigger: Parsing a method whose bytecode contains more than LOOP_HEADER_MAX_CAPACITY back edges / natural loops, e.g. machine-generated code with hundreds of thousands of loops.

Common situations: Bytecode generated by code generators, state-machine compilers, or obfuscators producing extremely high loop counts; pathological test cases (loop-unrolled source generated at build time); rarely, generated query/DSL compilers emitting one loop per case arm.

Related errors


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