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
- Reduce the number of loops in the offending method (split the method, simplify generated code, or disable extreme loop generation in the generator).
- Regenerate the class with the loops batched into inner helper methods.
- 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
- Bound loop counts in generated mega-methods (split into helper methods).
- Treat the LOOP_HEADER_MAX_CAPACITY sanity limit as a hard design constraint for generated code.
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
- Can not duplicate block with JSR data
- Irreducible
- Non-reducible loop requires too much duplication. Setting %s
- Block that is reached by a fall through end of code is reach
- cannot link call from %s
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/f65b30fa52d64345.
Report an issue: GitHub.