Punca dan Pengelakan java.lang.VerifyError

1. Pengenalan

Dalam tutorial ini, kita akan melihat penyebab ralat java.lang.VerifyError dan pelbagai cara untuk mengelakkannya.

2. Sebab

The Java Virtual Machine (JVM) distrusts semua bytecode dimuatkan sebagai prinsip teras Java Keselamatan Digital . Semasa runtime, JVM akan memuat fail .class dan berusaha menghubungkannya bersama-sama untuk membentuk yang dapat dilaksanakan - tetapi kesahihan fail .class yang dimuat ini tidak diketahui.

Untuk memastikan bahawa fail .class yang dimuat tidak menimbulkan ancaman kepada yang dapat dieksekusi terakhir, JVM melakukan pengesahan pada fail .class . Selain itu, JVM memastikan bahawa binari terbentuk dengan baik. Sebagai contoh, JVM akan mengesahkan kelas tidak menaip jenis kelas akhir .

Dalam banyak kes, pengesahan gagal pada kod bytek yang tidak berniat jahat kerana versi Java yang lebih baru mempunyai proses pengesahan yang lebih ketat daripada versi yang lebih lama . Sebagai contoh, JDK 13 mungkin telah menambahkan langkah pengesahan yang tidak ditegakkan dalam JDK 7. Oleh itu, jika kita menjalankan aplikasi dengan JVM 13 dan memasukkan kebergantungan yang disusun dengan versi Java Compiler (javac) yang lebih lama, JVM mungkin mempertimbangkan kebergantungan yang sudah lapuk menjadi tidak sah.

Oleh itu, semasa menghubungkan fail .class yang lebih lama dengan JVM yang lebih baru, JVM mungkin membuang java.lang.VerifyError yang serupa dengan yang berikut:

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 ...

Terdapat dua cara untuk menyelesaikan masalah ini:

  • Kemas kini pergantungan ke versi yang disusun dengan javac yang dikemas kini
  • Lumpuhkan pengesahan Java

3. Penyelesaian Pengeluaran

Penyebab ralat pengesahan yang paling biasa ialah menghubungkan binari menggunakan versi JVM yang lebih baru yang disusun dengan versi javac yang lebih lama . Ini lebih biasa apabila kebergantungan mempunyai bytecode yang dihasilkan oleh alat seperti Javassist , yang mungkin telah menghasilkan bytecode ketinggalan zaman jika alat itu ketinggalan zaman.

Untuk menyelesaikan masalah ini, kemas kini kebergantungan ke versi yang dibuat menggunakan versi JDK yang sepadan dengan versi JDK yang digunakan untuk membangun aplikasi . Sebagai contoh, jika kita membina aplikasi menggunakan JDK 13, kebergantungan harus dibina menggunakan JDK 13.

Untuk mencari versi yang serasi, periksa Build-Jdk dalam fail JAR Manifest kebergantungan untuk memastikannya sepadan dengan versi JDK yang digunakan untuk membina aplikasi.

4. Penyahpepijatan & Penyelesaian Pembangunan

Ketika melakukan debug atau mengembangkan aplikasi, kami dapat menonaktifkan pengesahan sebagai perbaikan cepat.

Jangan gunakan penyelesaian ini untuk kod pengeluaran .

Dengan melumpuhkan pengesahan, JVM dapat memautkan kod berbahaya atau rosak ke aplikasi kami, yang mengakibatkan kompromi keselamatan atau kerosakan ketika dijalankan.

Juga perhatikan bahawa pada JDK 13, penyelesaian ini sudah tidak digunakan lagi, dan kita tidak boleh mengharapkan penyelesaian ini dapat berfungsi pada rilis Java yang akan datang. Menonaktifkan pengesahan akan menghasilkan amaran 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 melumpuhkan pengesahan bytecode berbeza berdasarkan cara kami menjalankan kod kami.

4.1. Garis Perintah

Untuk melumpuhkan pengesahan pada baris perintah, hantarkan bendera noverify ke perintah java :

java -noverify Foo.class

Perhatikan bahawa -noverify adalah jalan pintas untuk -Xverify: tidak ada dan kedua-duanya boleh digunakan secara bergantian .

4.2. Maven

Untuk melumpuhkan pengesahan dalam build Maven, berikan bendera noverify ke mana-mana plugin yang diingini:

 com.example.baeldung example-plugin   -noverify   

4.3. Gradle

Untuk mematikan pengesahan dalam Gradle build, lulus bendera noverify ke tugas yang diinginkan:

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

5. Kesimpulan

Dalam tutorial ringkas ini, kami mengetahui mengapa JVM melakukan pengesahan bytecode dan apa yang menyebabkan ralat java.lang.VerifyError . Kami juga meneroka dua penyelesaian: Satu pengeluaran dan satu bukan pengeluaran.

Sekiranya boleh, gunakan versi kebergantungan terkini daripada mematikan pengesahan.