Oorzaken en vermijding van java.lang.VerifyError

1. Inleiding

In deze tutorial kijken we naar de oorzaak van java.lang.VerifyError fouten en meerdere manieren om dit te vermijden.

2. Oorzaak

De Java Virtual Machine (JVM) wantrouwt alle geladen bytecodes als een kernprincipe van het Java-beveiligingsmodel. Tijdens runtime wordt de JVM geladen .klasse bestanden en probeer ze aan elkaar te koppelen om een ​​uitvoerbaar bestand te vormen - maar de geldigheid hiervan is geladen .klasse bestanden is onbekend.

Om ervoor te zorgen dat de geladen .klasse bestanden vormen geen bedreiging voor het uiteindelijke uitvoerbare bestand, de JVM voert verificatie uit op het .klasse bestanden. Bovendien zorgt de JVM ervoor dat binaire bestanden goed worden gevormd. De JVM controleert bijvoorbeeld of klassen niet subtypen laatste klassen.

In veel gevallen mislukt verificatie op geldige, niet-schadelijke bytecode omdat een nieuwere versie van Java heeft een strikter verificatieproces dan oudere versies. JDK 13 kan bijvoorbeeld een verificatiestap hebben toegevoegd die niet werd afgedwongen in JDK 7. Dus als we een applicatie draaien met JVM 13 en afhankelijkheden opnemen die zijn gecompileerd met een oudere versie van de Java Compiler (javac), kan de JVM de verouderde afhankelijkheden zijn ongeldig.

Dus bij het koppelen van oudere .klasse bestanden met een nieuwere JVM, de JVM mag een java.lang.VerifyError vergelijkbaar met het volgende:

java.lang.VerifyError: Verwacht een stackmap-frame op vertakkingsdoel X Uitzonderingsdetails: Locatie: com / example / baeldung.Foo (Lcom / example / baeldung / Bar: Baz;) Lcom / example / baeldung / Foo; @ 1: infonull Reden: verwacht stackmapframe op deze locatie. Bytecode: 0000000: 0001 0002 0003 0004 0005 0006 0007 0008 0000010: 0001 0002 0003 0004 0005 0006 0007 0008 ...

Er zijn twee manieren om dit probleem op te lossen:

  • Werk afhankelijkheden bij naar versies die zijn gecompileerd met een bijgewerkt Javac
  • Schakel Java-verificatie uit

3. Productieoplossing

De meest voorkomende oorzaak van een verificatiefout is het koppelen van binaire bestanden met behulp van een nieuwere JVM-versie die is gecompileerd met een oudere versie van Javac. Dit komt vaker voor wanneer afhankelijkheden hebben bytecode gegenereerd door tools zoals Javassist, die mogelijk verouderde bytecode heeft gegenereerd als de tool verouderd is.

Om dit probleem op te lossen, update afhankelijkheden naar eenversie gebouwd met behulp van een JDK-versie die overeenkomt met de JDK-versie die is gebruikt om de applicatie te bouwen. Als we bijvoorbeeld een applicatie bouwen met JDK 13, moeten de afhankelijkheden worden gebouwd met JDK 13.

Om een ​​compatibele versie te vinden, controleert u het Build-Jdk in het JAR-manifestbestand van de afhankelijkheid om ervoor te zorgen dat deze overeenkomt met de JDK-versie die is gebruikt om de toepassing te bouwen.

4. Oplossing voor foutopsporing en ontwikkeling

Bij het debuggen of ontwikkelen van een applicatie kunnen we verificatie uitschakelen als een snelle oplossing.

Gebruik deze oplossing niet voor productiecode.

Door verificatie uit te schakelen, kan de JVM kwaadaardige of defecte code aan onze applicaties koppelen, wat resulteert in beveiligingsproblemen of crashes wanneer deze wordt uitgevoerd.

Merk ook op dat deze oplossing vanaf JDK 13 is verouderd, en we mogen niet verwachten dat deze oplossing werkt in toekomstige Java-releases. Als u verificatie uitschakelt, krijgt u de volgende waarschuwing:

Java HotSpot (TM) 64-bits server VM-waarschuwing: Opties -Xverify: none en -noverify zijn verouderd in JDK 13 en zullen waarschijnlijk in een toekomstige release worden verwijderd.

Het mechanisme voor het uitschakelen van bytecode-verificatie is afhankelijk van hoe we onze code uitvoeren.

4.1. Opdrachtregel

Om verificatie op de opdrachtregel uit te schakelen, geeft u het noverify vlag naar de Java opdracht:

java -noverify Foo.class

Let daar op -noverify is een snelkoppeling voor-Xverify: geen en beide kunnen door elkaar worden gebruikt.

4.2. Maven

Als u verificatie in een Maven-build wilt uitschakelen, geeft u het noverify markeer naar elke gewenste plug-in:

 com.example.baeldung voorbeeld-plugin -noverify 

4.3. Grijpen

Om verificatie in een Gradle-build uit te schakelen, geeft u het noverify vlag naar elke gewenste taak:

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

5. Conclusie

In deze korte tutorial hebben we geleerd waarom de JVM bytecode-verificatie uitvoert en wat de oorzaak is van java.lang.VerifyError fout. We hebben ook twee oplossingen onderzocht: een productie-oplossing en een niet-productie-oplossing.

Wanneer mogelijk, gebruik de nieuwste versies van afhankelijkheden in plaats van verificatie uit te schakelen.