«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/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 зависимости, чтобы убедиться, что он соответствует версии 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

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

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

4.3. Gradle

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

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

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

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

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