clojure/clojure · error · MethodTooLargeException

Method too large: .

Error message

Method too large: ${className}.${methodName} ${descriptor}

What it means

The JVM's class file format stores the Code attribute length in a 2-byte unsigned short, so any single method's bytecode may not exceed 65535 bytes. During ClassWriter.toByteArray(), MethodWriter detects code.length > 65535 and throws MethodTooLargeException naming the class, method and descriptor; this means the generator produced too much bytecode for one method, not that a parameter is wrong.

Solutions

  1. Split the generated code across multiple private helper methods invoked from the main one.
  2. Move large constant tables/data into static fields, arrays or resources loaded at runtime instead of inlined instructions.
  3. Reduce instrumentation scope: wrap the original method body in a try or delegate to a generated helper instead of inlining logic.
  4. For javac inputs, refactor the source method (extract methods, replace long switch chains with map lookups) and recompile.
  5. Use invokedynamic/MethodHandles to dispatch dynamically instead of generating huge switch statements.

Example fix

// before: emit every case inline in one giant method
for (Rule r : rules) { generateCaseInline(mv, r); } // >65535 bytes -> MethodTooLargeException
// after
generateDispatch(mv, rules); // emits tableswitch calling generated helpers
for (Rule r : rules) {
  MethodVisitor h = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, "rule$" + r.id, "()V", null, null);
  generateCaseInline(h, r);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Track approximate generated size while emitting; bail out early
private int approxBytes = 0;
void emit(Consumer<MethodVisitor> op, int estimatedBytes) {
  if (approxBytes + estimatedBytes > 60000) {
    throw new IllegalStateException("Generated method nearing 64KB code limit; split it");
  }
  approxBytes += estimatedBytes;
  op.accept(mv);
}

Try / catch

try {
  byte[] classBytes = classWriter.toByteArray();
} catch (MethodTooLargeException e) {
  // e gives className, methodName, descriptor, codeSize
  throw new IllegalStateException(
    "Split generated method " + e.getMethodName() + " (code size " + e.getCodeSize() + ") into helpers", e);
}

Prevention

When it happens

Trigger: Dynamic class generation or instrumentation that creates one huge method: giant switch/string dispatch, deeply inlined generated code, a huge static initializer (<clinit>) with many constants, or a bytecode weaver injecting a very large block into an already-large method.

Common situations: Compilers for DSLs (rule engines, query builders, protobuf-like serializers) generating one method per schema; test-code generators creating thousands of asserts in one method; annotation processors inlining large templates; instrumenting large legacy methods with substantial added prologue code.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of clojure/clojure@f3b143341d (2026-09-09). Data as JSON: /api/errors/fb127f6a03c19e4b. Report an issue: GitHub.

Appendix: source

Thrown at src/jvm/clojure/asm/MethodWriter.java:2087

  /**
   * Returns the size of the method_info JVMS structure generated by this MethodWriter. Also add the
   * names of the attributes of this method in the constant pool.
   *
   * @return the size in bytes of the method_info JVMS structure.
   */
  int computeMethodInfoSize() {
    // If this method_info must be copied from an existing one, the size computation is trivial.
    if (sourceOffset != 0) {
      // sourceLength excludes the first 6 bytes for access_flags, name_index and descriptor_index.
      return 6 + sourceLength;
    }
    // 2 bytes each for access_flags, name_index, descriptor_index and attributes_count.
    int size = 8;
    // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
    if (code.length > 0) {
      if (code.length > 65535) {
        throw new MethodTooLargeException(
                symbolTable.getClassName(), name, descriptor, code.length);
      }
      symbolTable.addConstantUtf8(Constants.CODE);
      // The Code attribute has 6 header bytes, plus 2, 2, 4 and 2 bytes respectively for max_stack,
      // max_locals, code_length and attributes_count, plus the bytecode and the exception table.
      size += 16 + code.length + Handler.getExceptionTableSize(firstHandler);
      if (stackMapTableEntries != null) {
        boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6;
        symbolTable.addConstantUtf8(useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap");
        // 6 header bytes and 2 bytes for number_of_entries.
        size += 8 + stackMapTableEntries.length;
      }
      if (lineNumberTable != null) {
        symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE);
        // 6 header bytes and 2 bytes for line_number_table_length.
        size += 8 + lineNumberTable.length;
      }
      if (localVariableTable != null) {

View on GitHub (pinned to f3b143341d)