Penyebab dan Penghindaran java.lang.VerifyError

1. Perkenalan

Dalam tutorial ini, kita akan melihat penyebab kesalahan java.lang.VerifyError dan beberapa cara untuk menghindarinya.

2. Penyebab

The Java Virtual Machine (JVM) tidak percaya semua bytecode dimuat sebagai prinsip inti dari Model Keamanan Java . Selama runtime, JVM akan memuat file .class dan mencoba menautkannya bersama untuk membentuk file yang dapat dieksekusi - tetapi validitas file .class yang dimuat ini tidak diketahui.

Untuk memastikan bahwa file .class yang dimuat tidak menimbulkan ancaman bagi file yang dapat dieksekusi, JVM melakukan verifikasi pada file .class . Selain itu, JVM memastikan bahwa binari terbentuk dengan baik. Misalnya, JVM akan memverifikasi kelas tidak subtipe kelas akhir .

Dalam banyak kasus, verifikasi gagal pada bytecode yang valid dan tidak berbahaya karena versi Java yang lebih baru memiliki proses verifikasi yang lebih ketat daripada versi yang lebih lama . Misalnya, JDK 13 mungkin telah menambahkan langkah verifikasi yang tidak diberlakukan di JDK 7. Jadi, jika kita menjalankan aplikasi dengan JVM 13 dan menyertakan dependensi yang dikompilasi dengan versi Java Compiler (javac) yang lebih lama, JVM dapat mempertimbangkan dependensi yang sudah ketinggalan zaman menjadi tidak valid.

Jadi, saat menautkan file .class lama dengan JVM yang lebih baru, JVM mungkin menampilkan java.lang.VerifyError yang mirip dengan berikut ini:

java.lang.VerifyError: Expecting a stackmap frame at branch target X Exception Details: Location: com/example/baeldung.Foo(Lcom/example/baeldung/Bar:Baz;)Lcom/example/baeldung/Foo; @1: infonull Reason: Expected stackmap frame at this location. Bytecode: 0000000: 0001 0002 0003 0004 0005 0006 0007 0008 0000010: 0001 0002 0003 0004 0005 0006 0007 0008 ...

Ada dua cara untuk mengatasi masalah ini:

  • Perbarui dependensi ke versi yang dikompilasi dengan javac yang diperbarui
  • Nonaktifkan verifikasi Java

3. Solusi Produksi

Penyebab paling umum dari kesalahan verifikasi adalah menautkan biner menggunakan versi JVM yang lebih baru yang dikompilasi dengan versi javac yang lebih lama . Ini lebih umum ketika dependensi memiliki bytecode yang dibuat oleh alat seperti Javassist , yang mungkin telah menghasilkan bytecode yang sudah ketinggalan zaman jika alat tersebut sudah usang.

Untuk mengatasi masalah ini, perbarui dependensi ke versi yang dibuat menggunakan versi JDK yang cocok dengan versi JDK yang digunakan untuk membangun aplikasi . Misalnya, jika kita membangun aplikasi menggunakan JDK 13, dependensi harus dibuat menggunakan JDK 13.

Untuk menemukan versi yang kompatibel, periksa Build-Jdk di file Manifes JAR dari dependensi untuk memastikan cocok dengan versi JDK yang digunakan untuk membuat aplikasi.

4. Solusi Debugging & Pengembangan

Saat men-debug atau mengembangkan aplikasi, kami dapat menonaktifkan verifikasi sebagai perbaikan cepat.

Jangan gunakan solusi ini untuk kode produksi .

Dengan menonaktifkan verifikasi, JVM dapat menautkan kode berbahaya atau salah ke aplikasi kami, yang mengakibatkan gangguan keamanan atau crash saat dijalankan.

Perhatikan juga bahwa mulai JDK 13, solusi ini sudah tidak digunakan lagi, dan kami tidak mengharapkan solusi ini berfungsi di rilis Java mendatang. Menonaktifkan verifikasi akan menghasilkan peringatan berikut:

Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.

Mekanisme untuk menonaktifkan verifikasi bytecode bervariasi berdasarkan cara kami menjalankan kode kami.

4.1. Garis komando

Untuk menonaktifkan verifikasi pada baris perintah, teruskan bendera noverify ke perintah java :

java -noverify Foo.class

Perhatikan bahwa -noverify adalah jalan pintas untuk -Xverify: none dan keduanya dapat digunakan secara bergantian .

4.2. Maven

Untuk menonaktifkan verifikasi di build Maven, teruskan flag noverify ke plugin apa pun yang diinginkan:

 com.example.baeldung example-plugin   -noverify   

4.3. Gradle

Untuk menonaktifkan verifikasi dalam build Gradle, teruskan tanda noverify ke tugas yang diinginkan:

someTask { // ... jvmArgs = jvmArgs << "-noverify" }

5. Kesimpulan

Dalam tutorial singkat ini, kita mempelajari mengapa JVM melakukan verifikasi bytecode dan apa yang menyebabkan kesalahan java.lang.VerifyError . Kami juga menjajaki dua solusi: satu produksi dan satu non-produksi.

Jika memungkinkan, gunakan versi dependensi terbaru daripada menonaktifkan verifikasi.