Java.Lang.VerifyError: オペランド スタックの型が正しくありません

Suraj P 2023年10月12日
  1. Java の java.lang.VerifyError を理解する
  2. Java での java.lang.VerifyError の例
Java.Lang.VerifyError: オペランド スタックの型が正しくありません

この記事では、Java の java.lang.VerifyError について学びます。

Java の java.lang.VerifyError を理解する

java.lang.VerifyError は、バイトコード検証プロセス中に Java 仮想マシン (JVM) で発生するエラーです。 このエラーは、クラスまたはメソッドのバイトコードが無効であるか、特定の制約に違反していることを JVM が検出した場合にスローされます。

java.lang.VerifyError が発生する理由はいくつかあります。

  1. 互換性のないクラス バージョン: クラスを実行しようとしている JVM のバージョンとは異なるバージョンの Java コンパイラでクラスがコンパイルされている場合、VerifyError が発生する可能性があります。 これは、クラスがより新しいバージョンの Java で構築された後、古い JVM で実行された場合に発生する可能性があります。
  2. ライブラリのバージョンに互換性がない: クラスがコンパイルされたバージョンとは異なるバージョンのライブラリにクラスが依存している場合、VerifyError が発生する可能性があります。
  3. オペコードの不正使用: クラスまたはメソッドがオペコードを不正またはサポートされていない方法で使用すると、VerifyError が発生する可能性があります。
  4. 無効なクラス ファイル形式: クラス ファイルが破損しているか、形式が間違っている場合、VerifyError が発生する可能性があります。
  5. final キーワードの無効な使用: 初期化後に final 変数に新しい値が割り当てられると、VerifyError が発生する可能性があります。
  6. リフレクションを使用して、別の classloader がクラスをロードするときにクラスのプライベート フィールド/メソッドのアクセシビリティを変更します。

このエラーを修正するには、問題の原因を見つけて修正する必要があります。 これには、互換性のあるバージョンの Java コンパイラを使用したクラスの再コンパイル、ライブラリ バージョンの更新、またはオペコードの不正使用の削除が含まれる場合があります。

エラーがリフレクションの使用によるものである場合、別の classloader によってロードされたクラスのプライベート フィールド/メソッドのアクセシビリティを変更しないことで回避できます。

一般に、使用されている Java コンパイラとライブラリのバージョンを認識し、さまざまなバージョンの JVM でコードをテストして互換性を確保することをお勧めします。 さらに、java.lang.VerifyError のようなエラーを回避するために、クラス ファイルとライブラリを最新の状態に保ち、良好な状態に保つことが重要です。

java.lang.VerifyError は実行時エラーであり、コンパイル時エラーである java.lang.VerificationError とは異なることに注意してください。

Java での java.lang.VerifyError の例

それをよりよく理解するために例を見てみましょう。

例 1: A.javaB.java という 2つの Java ファイルを作成します。

A.java ファイル:

public class A {
  public A() {
    System.out.println("Instance of A is created");
  }
}

B.java ファイル:

public class B extends A {
  public B() {
    super();
    System.out.println("Instance of B is created");
  }

  public static void main(String[] args) {
    B obj = new B();
  }
}

次に、ファイルをコンパイルします。

javac A.java
javac B.java

両方のファイルは、エラーなしで適切にコンパイルされます。

ただし、class A の定義を final に変更し、class A のみを再コンパイルしてから class Bmain メソッドを実行すると、次のエラーが発生します。

Exception in thread "main" java.lang.VerifyError: Cannot inherit from final class
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
     ......
     ......

このエラーは、class A の定義を変更したために発生しますが、class B は古いバージョンの class A で実行されます。

例 2:

3つの Java ファイル、A.javaB.java、および C.java を作成します。 class Bclass A を継承し、class Cmain メソッドを含みます。

A.java ファイル:

public class A {
  public A() {
    System.out.println("Class A instance is created");
  }

  public void print() {
    System.out.println("A::print()");
  }
}

B.java ファイル:

public class B extends A {
  public B() {
    super();
    System.out.println("Class B instance is created");
  }

  public void print() {
    System.out.println("B::print()");
  }
}

C.java ファイル:

public class C {
  public static void _print_(A obj) {
    obj.print();
  }

  public static void main(String[] args) {
    B b = new B();
    C._print_(b);
  }
}

Java ファイルを個別にコンパイルしてから、C.java を実行します。

javac A.java
javac B.java
javac C.java
java C

実行結果は次のとおりです。

Class A instance is created
Class B instance is created
B::print()

ここで、class A を拡張しないように class B の定義を変更し、B.java を再コンパイルして class C を実行すると、次のエラーが発生します。

Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
  ....
  ....

ここでも、class B の定義を変更したためにエラーが発生しますが、class B の古いバージョンで class C が実行されます。

著者: Suraj P
Suraj P avatar Suraj P avatar

A technophile and a Big Data developer by passion. Loves developing advance C++ and Java applications in free time works as SME at Chegg where I help students with there doubts and assignments in the field of Computer Science.

LinkedIn GitHub

関連記事 - Java Error