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 错误,都必须仔细检查相关的错误消息以及可以获取的任何堆栈跟踪,以便确定导致问题的具体问题并采取必要的纠正措施。

更新于: 2023-07-31

501 次浏览

启动您的 职业生涯

通过完成课程获得认证

开始学习
广告