Перейти к основному содержимому

Причины и способы предотвращения ошибки java.lang.VerifyError

· 4 мин. чтения

1. Введение

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

2. Причина

Виртуальная машина Java (JVM) не доверяет всему загруженному байт-коду, что является основным принципом модели безопасности Java . Во время выполнения JVM загрузит файлы .class и попытается связать их вместе, чтобы сформировать исполняемый файл, но достоверность этих загруженных файлов .class неизвестна.

Чтобы убедиться, что загруженные файлы .class не представляют угрозы для конечного исполняемого файла, JVM выполняет проверку файлов .class . Кроме того, JVM гарантирует правильность форматирования двоичных файлов. Например, JVM проверит, что классы не являются подтипами конечных классов.

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

Таким образом, при связывании старых файлов .class с более новой JVM JVM может выдать ошибку java.lang.VerifyError , аналогичную следующей:

java.lang.VerifyError: Expecting a stackmap frame at branch target X
Exception Details:
Location:

com/example/foreach.Foo(Lcom/example/foreach/Bar:Baz;)Lcom/example/foreach/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 зависимости, чтобы убедиться, что он соответствует версии 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 любому нужному плагину:

<plugin>
<groupId>com.example.foreach</groupId>
<artifactId>example-plugin</artifactId>
<!-- ... -->
<configuration>
<argLine>-noverify</argLine>
<!-- ... -->
</configuration>
</plugin>

4.3. Грейдл

Чтобы отключить проверку в сборке Gradle , передайте флаг noverify любой нужной задаче:

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

5. Вывод

В этом кратком руководстве мы узнали, почему JVM выполняет проверку байт-кода и что вызывает ошибку java.lang.VerifyError . Мы также рассмотрели два решения: производственное и непроизводственное.

По возможности используйте последние версии зависимостей , а не отключайте проверку.