Bekijk de bytecode van een klassebestand in Java

1. Overzicht

Bytecode-analyse is een gangbare praktijk onder Java-ontwikkelaars om vele redenen, zoals het vinden van problemen met code, codeprofilering en het doorzoeken van klassen met specifieke annotaties.

In dit artikel zullen we manieren onderzoeken om de bytecode van een klassebestand in Java te bekijken.

2. Wat is de bytecode?

Bytecode is de tussenliggende weergave van een Java-programma, waarmee een JVM een programma kan vertalen naar montage-instructies op machineniveau.

Wanneer een Java-programma wordt gecompileerd, wordt bytecode gegenereerd in de vorm van een .klasse het dossier. Dit .klasse bestand bevat niet-uitvoerbare instructies en is afhankelijk van een JVM die moet worden geïnterpreteerd.

3. Met behulp van javap

De Java-opdrachtregel wordt geleverd met de javap tool die informatie weergeeft over de velden, constructors en methoden van een klassenbestand.

Op basis van de gebruikte opties kan het een klasse uit elkaar halen en de instructies tonen die de Java-bytecode vormen.

3.1. javap

Laten we de javap commando om de bytecode van de meest voorkomende Voorwerp klasse:

$ javap java.lang.Object

De uitvoer van de opdracht zal de absolute minimumconstructie van het Voorwerp klasse:

openbare klasse java.lang.Object {openbare java.lang.Object (); openbare laatste native java.lang.Class getClass (); openbare native int hashCode (); openbare boolean is gelijk aan (java.lang.Object); beschermde native java.lang.Object clone () gooit java.lang.CloneNotSupportedException; openbare java.lang.String toString (); openbare definitieve native ongeldig melden (); openbare definitieve native void notificationAll (); openbare laatste native void wait (lang) gooit java.lang.InterruptedException; public final void wait (long, int) gooit java.lang.InterruptedException; public final void wait () gooit java.lang.InterruptedException; protected void finalize () gooit java.lang.Throwable; statisch {}; }

Standaard bevat de bytecode-uitvoer geen velden / methoden met een prive toegangsmodificator.

3.2. javap-p

Om alle klassen en leden te zien, kunnen we de -p argument:

openbare klasse java.lang.Object {openbare java.lang.Object (); privé statisch native ongeldig registerNatives (); openbare laatste native java.lang.Class getClass (); openbare native int hashCode (); openbare boolean is gelijk aan (java.lang.Object); beschermde native java.lang.Object clone () gooit java.lang.CloneNotSupportedException; // ...}

Hier kunnen we een privaat methode registerNatives wordt ook weergegeven in de bytecode van de Voorwerp klasse.

3.3. javap-v

Evenzo kunnen we de -v argument om uitgebreide informatie zoals stapelgrootte en argumenten voor methoden van de Voorwerp klasse:

Classfile jar: file: /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar! /Java/lang/Object.class Laatst gewijzigd op 15 maart 2017; grootte 1497 bytes MD5 checksum 5916745820b5eb3e5647da3b6cc6ef65 Gecompileerd van "Object.java" openbare klasse java.lang.Object secundaire versie: 0 hoofdversie: 52 vlaggen: ACC_PUBLIC, ACC_SUPER Constante pool: # 1 = Klasse # 49 // java / lang / StringBuilder / / ... {openbare java.lang.Object (); descriptor: () V vlaggen: ACC_PUBLIC Code: stack = 0, locals = 1, args_size = 1 0: return LineNumberTable: regel 37: 0 publieke finale native java.lang.Class getClass (); descriptor: () Ljava / lang / Class; vlaggen: ACC_PUBLIC, ACC_FINAL, ACC_NATIVE Handtekening: # 26 // () Ljava / lang / Class; // ...} SourceFile: "Object.java"

3.4. javap-c

Ook de javap commando staat het demonteren van de hele Java-klasse toe met behulp van de -c argument:

Gecompileerd uit "Object.java" openbare klasse java.lang.Object {openbare java.lang.Object (); Code: 0: retourneer openbare booleaanse is gelijk aan (java.lang.Object); Code: 0: aload_0 1: aload_1 2: if_acmpne 9 5: iconst_1 6: goto 10 9: iconst_0 10: ireturn beschermde native java.lang.Object kloon () gooit java.lang.CloneNotSupportedException; // ...}

Verder is de javap commando stelt ons in staat om de systeeminformatie, constanten en interne typehandtekeningen te controleren met verschillende argumenten.

We kunnen alle argumenten opsommen die worden ondersteund door de javap commando met behulp van de -helpen argument.

Nu we een Java-opdrachtregeloplossing hebben gezien voor het bekijken van de bytecode van een klassebestand, gaan we eens kijken naar enkele bytecode-manipulatiebibliotheken.

4. ASM gebruiken

ASM is een populair prestatiegericht Java-bytecodemanipulatie- en analysekader op laag niveau.

4.1. Opstelling

Laten we eerst het laatste toevoegen asm en asm-util Maven afhankelijkheden van onze pom.xml:

 org.ow2.asm asm 8.0.1 org.ow2.asm asm-util 8.0.1 

4.2. Bekijk Bytecode

Vervolgens gebruiken we de ClassReader en TraceClassVisitor om de bytecode van het Voorwerp klasse:

probeer {ClassReader reader = new ClassReader ("java.lang.Object"); StringWriter sw = nieuwe StringWriter (); TraceClassVisitor tcv = nieuwe TraceClassVisitor (nieuwe PrintWriter (System.out)); reader.accept (tcv, 0); } catch (IOException e) {e.printStackTrace (); }

Hier zullen we opmerken dat de TraceClassVisitor object vereist het PrintWriter object om de bytecode te extraheren en te produceren:

// class version 52.0 (52) // toegangsvlaggen 0x21 public class java / lang / Object {// gecompileerd vanuit: Object.java // toegangsvlaggen 0x1 public () V L0 LINENUMBER 37 L0 RETURN MAXSTACK = 0 MAXLOCALS = 1 / / access flags 0x101 public native hashCode () I // access flags 0x1 public equals (Ljava / lang / Object;) Z L0 LINENUMBER 149 L0 ALOAD 0 ALOAD 1 IF_ACMPNE L1 ICONST_1 GOTO L2 L1 // ...}

5. BCEL gebruiken

De Byte Code Engineering Library, in de volksmond bekend als Apache Commons BCEL, biedt een gemakkelijke manier om Java-klassebestanden te maken / manipuleren.

5.1. Afhankelijkheid van Maven

Laten we zoals gewoonlijk het laatste toevoegen bcel Maven afhankelijkheid van onze pom.xml:

 org.apache.bcel bcel 6.5.0 

5.2. Demonteer klasse en bekijk bytecode

Vervolgens kunnen we de Opslagplaats class om het JavaClass voorwerp:

probeer {JavaClass objectClazz = Repository.lookupClass ("java.lang.Object"); System.out.println (objectClazz.toString ()); } catch (ClassNotFoundException e) {e.printStackTrace (); }

Hier hebben we de toString methode op de objectClazz object om bytecode in een beknopt formaat te zien:

public class java.lang.Object bestandsnaam java.lang.Object gecompileerd vanuit Object.java compilerversie 52.0 toegangsvlaggen 33 constante pool 78 vermeldingen ACC_SUPER vlag true Attribuut (en): SourceFile: Object.java 14 methoden: public void () privé static native void registerNatives () public final native Class getClass () [Signature: () Ljava / lang / Class;] public native int hashCode () public boolean is gelijk aan (Object arg1) beschermd native Object clone () throws Uitzonderingen: java.lang .CloneNotSupportedException public String toString () public final native void notification () // ...

Verder is de JavaClass class biedt methoden zoals getConstantPool, getFields, en getMethods om de details van de gedemonteerde klas te bekijken.

assertEquals (objectClazz.getFileName (), "java.lang.Object"); assertEquals (objectClazz.getMethods (). length, 14); assertTrue (objectClazz.toString (). bevat ("openbare klasse java.lang.Object")); 

Evenzo set * methoden zijn beschikbaar voor bytecode-manipulatie.

6. Javassist gebruiken

We kunnen ook de Javassist (Java-programmeerassistent) bibliotheek die API's op hoog niveau biedt om Java-bytecode te bekijken / manipuleren.

6.1. Afhankelijkheid van Maven

Eerst voegen we de laatste toe javassist Maven afhankelijkheid van onze pom.xml:

 org.javassist javassist 3.27.0-GA 

6.2. Genereer ClassFile

Vervolgens kunnen we de ClassPool en ClassFile klassen om een ​​Java-klasse te genereren:

probeer {ClassPool cp = ClassPool.getDefault (); ClassFile cf = cp.get ("java.lang.Object"). GetClassFile (); cf.write (nieuwe DataOutputStream (nieuwe FileOutputStream ("Object.class"))); } catch (NotFoundException e) {e.printStackTrace (); }

Hier hebben we de schrijven methode, waarmee we het class-bestand kunnen schrijven met behulp van de DataOutputStream voorwerp:

// Gecompileerd vanuit Object.java (versie 1.8: 52.0, superbit) public class java.lang.Object {// Method descriptor # 19 () V // Stack: 0, Locals: 1 public Object (); 0 retourneren Regelnummers: [pc: 0, regel: 37] // Methode descriptor # 19 () V privé statisch native void registerNatives (); // Methode descriptor # 24 () Ljava / lang / Class; // Handtekening: () Ljava / lang / Class; openbare laatste native java.lang.Class getClass (); // Method descriptor # 28 () I public native int hashCode (); // ...

Ook is het doel van de ClassFile klasse biedt toegang tot de constante pool, velden en methoden:

assertEquals (cf.getName (), "java.lang.Object"); assertEquals (cf.getMethods (). size (), 14);

7. Jclasslib

Bovendien kunnen we een op IDE gebaseerde plug-in gebruiken om de bytecode van een klassebestand te bekijken. Laten we bijvoorbeeld eens kijken naar het jclasslib Bytecode-kijker plug-in beschikbaar voor IntelliJ IDEA.

7.1. Installatie

Eerst installeren we de plug-in met behulp van het dialoogvenster Instellingen / Voorkeuren:

7.2. Bekijk Bytecode van het Voorwerp Klasse

Vervolgens kunnen we de optie "Show Bytecode With Jclasslib" kiezen onder het menu View om de bytecode van de geselecteerde Voorwerp klasse:

Vervolgens wordt een dialoogvenster geopend om de bytecode van het Voorwerp klasse:

7.3. Details bekijken

We kunnen ook verschillende details van de bytecode zien, zoals constante pool, velden en methoden met behulp van het dialoogvenster Jclasslib-plug-in:

Evenzo hebben we de Bytecode Visualizer-plug-in om de bytecode van een klassebestand te bekijken met de Eclipse IDE.

8. Conclusie

In deze tutorial hebben we manieren onderzocht om de bytecode van een klassebestand in Java te bekijken.

Eerst hebben we de javap commando samen met zijn verschillende argumenten. Vervolgens hebben we enkele bytecode-manipulatiebibliotheken doorgenomen die de functies bieden om de bytecode te bekijken en te manipuleren.

Als laatste hebben we gekeken naar een IDE-gebaseerde plug-in Jclasslib waarmee we bytecode kunnen bekijken in IntelliJ IDEA.

Zoals gewoonlijk zijn alle code-implementaties beschikbaar op GitHub.