类加载时 JVM 会用字节码验证器检查类型安全。验证什么、什么时候验证、关掉它会怎样?这篇用 ASM 造出七种坏 class,真实触发每一种 VerifyError,再量验证的成本,并看关闭验证后的后果。
A 栈下溢:iadd 时栈是空的 → defineClass 成功,getMethod 时抛出 VerifyError: Operand stack underflow ← Attempt to pop empty stack. B 返回类型不符:iconst_1 再 areturn(方法声明返回 int) → defineClass 成功,getMethod 时抛出 VerifyError: Bad type on operand stack ← Type integer (current frame, stack[0]) is not assignable to reference type C 局部变量越界:静态无参方法里 iload_0 → defineClass 成功,getMethod 时抛出 VerifyError: Local variable table overflow ← Local index 0 is invalid D 对没有调用 <init> 的对象调用 hashCode(new Object 后直接 invokevirtual) → defineClass 成功,getMethod 时抛出 VerifyError: Bad type on operand stack ← Type uninitialized 0 (current frame, stack[1]) is not assignable to 'java/lang/Object' E 分支目标没有栈映射帧(class 版本 52) → defineClass 成功,getMethod 时抛出 VerifyError: Expecting a stackmap frame at branch target 6 ← Expected stackmap frame at this location. F 方法末尾没有返回(执行会掉出代码末端) → defineClass 成功,getMethod 时抛出 VerifyError: Control flow falls through code end ← Error exists in the bytecode G max_stack 声明得太小(实际要 2,声明 1) → defineClass 成功,getMethod 时抛出 VerifyError: Operand stack overflow ← Exceeded max stack size.
defineClass(含有验证不通过的方法的类)→ 成功,返回 Class 对象 t.Bad(此时还没有验证) Class.forName(name, initialize=false, loader) → 成功(没有触发链接) getDeclaredMethods() → VerifyError: Bad type on operand stack getDeclaredConstructor() → VerifyError: Bad type on operand stack 调用 f() → VerifyError 第二次调用 → VerifyError(同样失败,每次都是同一个验证错误) Class.forName(name, true, loader)→ VerifyError 对照:合法的类调用 f() = 1 class 版本 49(Java 5,没有栈映射帧,走旧的类型推断验证器)同一个错误 → VerifyError: (class: t/Old, method: f signature: ()I) Expecting to find object/array on stack
[默认(验证应用类)] 3 次运行(每次内部取最好)的中位:41 ms,每个类 14 μs;范围 40–42 ms [-XX:+UnlockDiagnosticVMOptions -XX:-BytecodeVerificationRemote(不验证应用类加载器的类)] 3 次运行(每次内部取最好)的中位:30 ms,每个类 10 μs;范围 29–30 ms JVM 输出的警告: [] 不验证(-XX:-BytecodeVerificationRemote)时,iconst_1 再 areturn 的类(验证本来会拒绝):JVM 返回码 -6 ;输出: ['# A fatal error has been detected by the Java Runtime Environment:', '# Internal Error (0xe0000000), pid=9619, tid=5635', '# stop: illegal bytecode sequence - method not verified', '# JRE version: OpenJDK Runtime Environment Homebrew (21.0.12.1) (build 21.0.12.1)', '# Java VM: OpenJDK 64-Bit Server VM Homebrew (21.0.12.1, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)', '# Problematic frame:'] -Xverify:none(已弃用)时同一个类:JVM 返回码 -6 ;输出: ['# A fatal error has been detected by the Java Runtime Environment:', '# Internal Error (0xe0000000), pid=9632, tid=5379', '# stop: illegal bytecode sequence - method not verified', '# JRE version: OpenJDK Runtime Environment Homebrew (21.0.12.1) (build 21.0.12.1)', '# Java VM: OpenJDK 64-Bit Server VM Homebrew (21.0.12.1, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, bsd-aarch64)', '# Problematic frame:']