Java 中的 java.lang.VerifyError 错误是如何发生的以及如何解决?
java.lang.VerifyError 是一个由 JVM(Java 虚拟机)产生的运行时错误。在运行时,会进行一个验证过程来检查加载的 .class 文件的有效性,如果 .class 文件违反了任何约束,则 JVM 会抛出 java.lang.VerifyError 错误。此错误存在于 Java 1.0 及更高版本中。
java.lang.LinkageError 扩展了 Java.lang.VerifyError,指的是程序链接过程中出现问题。
输出中的 java.lang.VerifyError 将类似于以下形式:
Exception in thread "main" java.lang.VerifyError: (class: com/example/Way2Class, method: myMethod signature: ()V) Incompatible argument to function at com.example.MyClass.main(MyClass.java:10)
此输出包含导致错误的类的名称 (Way2class)、类路径 (com/example/Way2Class)、错误所在的行号 (10)、方法名称 (myMethod) 和错误消息 (Exception in thread "main" java.lang.VerifyError)。
此错误可能由于多种原因导致,如下所示:
版本不匹配:需要注意的是,如果使用与执行代码所使用的 Java 编译器不同的 Java 编译器版本编译了类文件,则可能会导致 VerifyError 错误。
示例
public class VerifyErrorVersionExample { public static void main(String[] args) { System.out.println("Hello, program!"); } }
如果使用 Java 9 编译此特定程序,并在较早的版本(如 Java 8)上运行,则输出将显示以下错误说明:
输出
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack Exception Details: Location: VerifyErrorVersionExample.main([Ljava/lang/String;)V @2: invokestatic Reason: Type 'java/lang/invoke/StringConcatFactory' (current frame, stack[0]) is not assignable to 'java/lang/invoke/MethodHandle' Current Frame: bci: @2 flags: { } locals: { '[Ljava/lang/String;' } stack: { 'java/lang/invoke/StringConcatFactory' } Bytecode: 0x0000000: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 0x0000003: ldc #3 // String Hello, world! 0x0000005: invokestatic #4 // Method java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; 0x000000a: ldc #5 // String 0x000000c: iconst_1 0x000000d: anewarray #6 // class java/lang/Object 0x0000010: dup 0x0000011: iconst_0 0x0000012: ldc #7 // String !!! 0x0000014: aastore 0x0000015: invokevirtual #8 // Method java/lang/invoke/MethodHandle.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object; 0x000001a: checkcast #9 // class java/lang/String 0x000001d: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 0x0000020: return
字节码损坏:如果对类文件的字节码进行了修改或损坏,则可能会出现错误。
示例
public class VerifyErrorBytecodeExample { public static void main(String[] args) { int a = 10; int b = 20; int c = a + b; System.out.println("The sum of " + a + " and " + b + " is " + c); } }
输出
Exception in thread "main" java.lang.VerifyError: (class: VerifyErrorBytecodeExample, method: main signature: ([Ljava/lang/String;)V) Illegal target of jump or branch at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) at java.lang.Class.getDeclaredMethod(Class.java:2128) at java.io.ObjectStreamClass.getPrivateMethod(ObjectStreamClass.java:1743) at java.io.ObjectStreamClass.access$1700(ObjectStreamClass.java:72) at java.io.ObjectStreamClass$2.run(ObjectStreamClass.java:513) at java.io.ObjectStreamClass$2.run(ObjectStreamClass.java:492) at java.security.AccessController.doPrivileged(Native Method) at java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:492) at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:389) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1134) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at VerifyErrorBytecodeExample.main(VerifyErrorBytecodeExample.java:6)
类文件不一致:如果类文件违反了 Java 虚拟机规范中规定的某些约束。
示例
public class VerifyErrorExample { public static void main(String[] args) { Animal animal = new Cat(); animal.speak(); } } interface Animal { void speak(); } class Dog implements Animal { public void speak() { System.out.println("Woof!"); } } class Cat extends Dog { public void speak() { System.out.println("Meow!"); } }
此程序定义了一个“Animal”接口以及两个实现它的具体类:Dog 和 Cat。Cat 扩展了 Dog 并覆盖了它的 speak 方法。在 main 方法中,我们创建了一个 Cat 实例并调用了它的 speak 方法。
假设我们选择从当前类路径中移除 Dog 类,然后重新编译并运行程序,将会显示 java.lang.VerifyError 错误。此错误将包含如下错误消息:
输出
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack Exception Details: Location: VerifyErrorExample.main([Ljava/lang/String;)V @2: invokevirtual Reason: Type 'Cat' (current frame, stack[0]) is not assignable to 'Dog' Current Frame: bci: @2 flags: { } locals: { '[Ljava/lang/String;' } stack: { 'Cat' } Bytecode: 0000000: new #2 // class Cat 0000003: dup 0000004: invokespecial #3 // Method Cat."":()V 0000007: astore_1 0000008: aload_1 0000009: invokevirtual #4 // Method Animal.speak:()V 000000c: return
结论
总之,遇到 java.lang.VerifyError 错误可能源于各种原因,从字节码变化到安全计算实践中根深蒂固的限制,再到代码库中包含的不一致的相互依赖的 Java 组件版本,以及运行时库兼容性问题等。避免此问题的关键方法是确保所有使用的库都与您的系统和其他依赖库在运行时兼容。遵守这些要求将确保符合 Java 的字节码规范,避免出现问题代码实践,例如不遵守安全协议或提交类文件中不一致的代码,这可能会导致后续错误。无论何时遇到 java.lang.VerifyError 错误,都必须仔细检查相关的错误消息以及可以获取的任何堆栈跟踪,以便确定导致问题的具体问题并采取必要的纠正措施。