Причини и избягване на java.lang.VerifyError

1. Въведение

В този урок ще разгледаме причината за грешките в java.lang.VerifyError и множество начини да го избегнем.

2. Причина

В Java Virtual Machine (JVM) не вярва на всички заредени байткод като основен принцип на модел за сигурност на Java . По време на изпълнение JVM ще зареди .class файлове и ще се опита да ги свърже заедно, за да образува изпълним файл, но валидността на тези заредени .class файлове е неизвестна.

За да се гарантира, че заредените .class файлове не представляват заплаха за крайния изпълним файл, JVM извършва проверка на .class файловете. Освен това JVM гарантира, че двоичните файлове са добре оформени. Например JVM ще провери, че класовете не подтипират крайните класове.

В много случаи проверката се проваля на валиден, не злонамерен байт код, тъй като по-новата версия на Java има по-строг процес на проверка от по-старите версии . Например JDK 13 може да е добавил стъпка за проверка, която не е била приложена в JDK 7. По този начин, ако стартираме приложение с JVM 13 и включим зависимости, компилирани с по-стара версия на Java Compiler (javac), JVM може да разгледа остарелите зависимости да бъдат невалидни.

По този начин, когато свързвате по-стари .class файлове с по-нова JVM, JVM може да хвърли java.lang.VerifyError, подобно на следното:

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

Има два начина за решаване на този проблем:

  • Актуализирайте зависимостите до версии, компилирани с актуализиран javac
  • Деактивирайте потвърждаването на Java

3. Производствено решение

Най-честата причина за грешка при проверката е свързването на двоични файлове с помощта на по-нова версия на JVM, компилирана с по-стара версия на javac . Това е по-често, когато зависимостите имат байт код, генериран от инструменти като Javassist , който може да е генерирал остарял байт код, ако инструментът е остарял.

За да разрешите този проблем, актуализирайте зависимостите до версия, изградена с помощта на версия JDK, която съответства на версията JDK, използвана за изграждане на приложението . Например, ако изградим приложение, използвайки JDK 13, зависимостите трябва да бъдат изградени с помощта на JDK 13.

За да намерите съвместима версия, проверете Build-Jdk във файла JAR Manifest на зависимостта, за да се уверите, че съвпада с версията JDK, използвана за изграждане на приложението.

4. Решение за отстраняване на грешки и разработка

Когато отстраняваме грешки или разработваме приложение, можем да деактивираме проверката като бърза корекция.

Не използвайте това решение за производствен код .

Като деактивира проверката, JVM може да свързва злонамерен или дефектен код с нашите приложения, което води до компромиси със сигурността или сривове при изпълнение.

Също така имайте предвид, че от JDK 13 това решение е остаряло и не бива да очакваме това решение да работи в бъдещи версии на Java. Деактивирането на проверката ще доведе до следното предупреждение:

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.

Механизмът за деактивиране на проверката на байт кода варира в зависимост от начина, по който изпълняваме нашия код.

4.1. Командна линия

За да деактивирате проверката в командния ред, предайте флага noverify на командата на java :

java -noverify Foo.class

Имайте предвид, че -noverify е пряк път за -Xverify: none и двете могат да се използват взаимозаменяемо .

4.2. Мейвън

За да деактивирате проверката в компилация на Maven, предайте знамето на noverify на който и да е приставка:

 com.example.baeldung example-plugin   -noverify   

4.3. Градле

За да деактивирате проверката в компилация на Gradle, предайте знамето на noverify на всяка желана задача:

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

5. Заключение

В този бърз урок научихме защо JVM извършва проверка на байт кода и какво причинява грешката java.lang.VerifyError . Проучихме и две решения: производствено и непроизводствено.

Когато е възможно, използвайте най-новите версии на зависимостите, вместо да деактивирате проверката.