¿Por qué ocurre java.lang.VerifyError en Java y cómo resolverlo?

La máquina virtual de Java (JVM) desconfía de todos los códigos de bytes cargados como un principio fundamental del modelo de seguridad de Java. Durante el tiempo de ejecución, la JVM cargará archivos .class e intentará vincularlos para formar un ejecutable, pero se desconoce la validez de estos archivos .class cargados. Para asegurarse de que los archivos .class cargados no representen una amenaza para el ejecutable final, la JVM realiza un proceso de verificación en los archivos .class.

Además, la JVM garantiza que los archivos binarios estén bien formados. Por ejemplo, la JVM verificará que las clases no subtipifiquen las clases finales. En muchos casos, la verificación falla en un código de bytes válido y no malicioso porque una versión más nueva de Java tiene un proceso de verificación más estricto que las versiones anteriores. Por ejemplo, JDK 13 puede haber agregado un paso de verificación que no se aplicó en JDK 7. Por lo tanto, si ejecutamos una aplicación con JVM 13 e incluimos dependencias compiladas con una versión anterior del Compilador de Java (javac), la JVM puede considerar el dependencias desactualizadas para que no sean válidas.

Por lo tanto, al vincular archivos .class antiguos con una JVM más nueva, la JVM puede generar un java.lang.VerifyError.

El VerifyError existe desde la versión 1.0 de Java.

La estructura de VerifyError:

Constructores

VerifyError()

Este constructor crea una instancia de la clase VerifyError y establece nulo como su mensaje.

VerifyError(String s)

Este constructor crea una instancia de la clase VerifyError, utilizando la string especificada como mensaje. Aquí, la clase que arrojó el error se indica mediante un argumento de string.

Las tres razones más comunes por las que puede ocurrir este error son las siguientes:

Razón 1: «Este error se lanzará cada vez que se amplíe una clase declarada como final». 

Programa:

Java

// Java program to show the occurrence
// of  java.lang.VerifyError
 
class B extends A {
 
    public static void main(String args[])
 
    {
 
        System.out.println("my super class name:-"
                           + myname);
    }
}
 
public class A
 
{
 
    static String myname = "A";
}

Como ves, si compilas estos dos programas y los ejecutas, debe funcionar bien sin mostrar ningún error. Ahora, después de cambiar la clase A de la siguiente manera y compilarlo solo. 

final public class A
{
  static String myname="A";
}

Tenga en cuenta que aquí hemos recopilado solo la «clase A». Ahora, si ejecutamos la clase B (clase que contiene el método main()), se lanzará un mensaje de error como el siguiente en tiempo de ejecución.

Exception in thread "main" java.lang.VerifyError: Cannot inherit from final class
       at java.lang.ClassLoader.defineClass1(Native Method)
       at java.lang.ClassLoader.defineClassCond(Unknown Source)
       at java.lang.ClassLoader.defineClass(Unknown Source)
       at java.security.SecureClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.access$000(Unknown Source)
       at java.net.URLClassLoader$1.run(Unknown Source)
       at java.security.AccessController.doPrivileged(Native Method)
       at java.net.URLClassLoader.findClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
       at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: B.  

Este error se debió a que cambiamos la definición de la clase TestClassA, pero la clase TestClassB se compiló con una versión anterior de la clase TestClassA.

Razón 2: «Considere una clase que extienda otra clase antes y si ya no extiende esa clase ahora, entonces este error puede aparecer en tiempo de ejecución».

Programa:

Java

// Java program to show the occurrence
// of  java.lang.VerifyError
 
class C extends B {
    public static void main(String args[])
    {
        B b = new B();
        display(b);
    }
    public static void display(A a)
    {
        System.out.println(a.supername);
    }
}
 
class B extends A {
    String subname = "B";
}
 
public class A {
    String supername = "A";
}
Producción

A

El programa anterior también funcionará bien, pero si la clase B se cambia para que ya no extienda la clase A, es posible que se produzca un error. Ahora, si cambiamos la clase B de la siguiente manera y la «recompilamos sola», entonces la clase C no tendrá idea de los cambios realizados en la clase B, lo que causará este error.

class B {
String subname="B";
}

Excepción en el hilo «principal» java.lang.VerifyError: (clase: C, método: firma principal: ([Ljava/lang/String;)V) Argumento incompatible con la función

No se pudo encontrar la clase principal: C.  

Programa saldrá.

Razón 3: «Si intentamos anular un método que se declara como final, también se generará este error». Tengamos las clases A y B de la siguiente manera:

Programa:

Java

// Java program to show the occurrence
// of  java.lang.VerifyError
 
class B extends A
{
 
    public static void main(String args[])
 
    {
 
        A a = new A();
 
        a.display();
    }
 
    void display() { super.display(); }
}
 
public class A
{
    String supername = "A";
 
    void display()
 
    {
 
        System.out.println("My name is " + supername);
    }
}

En la clase A, si el método display() se cambia para que sea final y «recompilarlo solo», se generará este error de verificación si se ejecuta la clase B, ya que ninguna otra clase puede anular este método.

Producción:

Exception in thread "main" java.lang.VerifyError: class B overrides final method
display.()V
       at java.lang.ClassLoader.defineClass1(Native Method)
       at java.lang.ClassLoader.defineClassCond(Unknown Source)
       at java.lang.ClassLoader.defineClass(Unknown Source)
       at java.security.SecureClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.access$000(Unknown Source)
       at java.net.URLClassLoader$1.run(Unknown Source)
       at java.security.AccessController.doPrivileged(Native Method)
       at java.net.URLClassLoader.findClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
       at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: B.  Program will exit.

Aquí podría haber notado que esto verifica que se arroja un error porque hemos recompilado solo la clase editada y no todas las clases en su conjunto. Por lo tanto, puede pensar que este error se puede identificar fácilmente si vuelve a compilar todas las clases como un todo al volver a compilar la clase que contiene el método main(). Por supuesto, es cierto, pero hay ciertas situaciones en las que no puede identificar este error en el momento de la compilación, lo que se debe principalmente al uso de dos versiones diferentes de bibliotecas de terceros en su aplicación. 

¿Cómo lidiar con el VerifyError?

Para evitar el VerifyError, debe compilar todas sus clases usando la misma versión de Java. Además, una vez que se realiza un cambio en una clase, asegúrese de volver a compilar su proyecto desde cero. Finalmente, si tu aplicación hace uso de librerías externas, verifica que usas la versión adecuada de cada librería y por supuesto, consulta los javadocs correspondientes, para asegurarte de que todo está correcto.

Siempre que sea posible, use las últimas versiones de las dependencias en lugar de deshabilitar la verificación.

Publicación traducida automáticamente

Artículo escrito por nikhiltanna33 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *