Se Bytecode for en klassefil i Java

1. Oversigt

Bytecode-analyse er en almindelig praksis blandt Java-udviklere af mange grunde, som at finde problemer med kode, kodeprofilering og søge klasser med specifikke kommentarer.

I denne artikel undersøger vi måder at se bytekoden til en klassefil i Java.

2. Hvad er Bytecode?

Bytecode er den mellemliggende gengivelse af et Java-program, der gør det muligt for en JVM at oversætte et program til maskininstruktioner på maskinniveau.

Når et Java-program er kompileret, genereres bytecode i form af en .klasse fil. Det her .klasse filen indeholder instruktioner, der ikke kan køres, og er afhængige af, at en JVM skal fortolkes.

3. Brug javap

Java-kommandolinjen leveres med javap værktøj, der viser oplysninger om felter, konstruktører og metoder til en klassefil.

Baseret på de anvendte muligheder kan den adskille en klasse og vise de instruktioner, der indeholder Java-bytecode.

3.1. javap

Lad os bruge javap kommando for at se bytekoden for det mest almindelige Objekt klasse:

$ javap java.lang.Object

Outputtet af kommandoen viser det absolutte minimum konstruktion af Objekt klasse:

offentlig klasse java.lang.Object {offentlig java.lang.Object (); offentlig endelig native java.lang.Class getClass (); offentlig indfødt int hashCode (); offentlige boolske lig (java.lang.Object); beskyttet indfødte java.lang.Object klon () kaster java.lang.CloneNotSupportedException; offentlig java.lang.String tilString (); offentlig endelig indfødt ugyldig meddelelse (); offentlig endelig native void notifyAll (); offentlig endelig native ugyldig ventetid (lang) kaster java.lang.InterruptedException; offentlig endelig ugyldig ventetid (lang, int) kaster java.lang.InterruptedException; offentlig endelig ugyldig ventetid () kaster java.lang.InterruptedException; beskyttet tomrum finalize () kaster java.lang.Throwable; statisk {}; }

Som standard indeholder bytecode-output ikke felter / metoder med en privat adgangsmodifikator.

3.2. javap-p

For at se alle klasser og medlemmer kan vi bruge -p argument:

offentlig klasse java.lang.Object {offentlig java.lang.Object (); privat statisk native void registerNatives (); offentlig endelig native java.lang.Class getClass (); offentlig indfødt int hashCode (); offentlige boolske lig (java.lang.Object); beskyttet indfødte java.lang.Object klon () kaster java.lang.CloneNotSupportedException; // ...}

Her kan vi observere en privat metode registerNatives vises også i bytekoden til Objekt klasse.

3.3. javap-v

På samme måde kan vi bruge -v argument for at se detaljerede oplysninger som stakstørrelse og argumenter for metoder til Objekt klasse:

Classfile jar: file: /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar! /Java/lang/Object.class Sidst ændret 15. marts 2017; størrelse 1497 bytes MD5 kontrolsum 5916745820b5eb3e5647da3b6cc6ef65 Kompileret fra "Object.java" offentlig klasse java.lang.Object mindre version: 0 større version: 52 flag: ACC_PUBLIC, ACC_SUPER Konstant pool: # 1 = klasse # 49 // java / lang / StringBuilder / / ... {public java.lang.Object (); deskriptor: () V flags: ACC_PUBLIC Code: stack = 0, locals = 1, args_size = 1 0: return LineNumberTable: line 37: 0 public final native java.lang.Class getClass (); deskriptor: () Ljava / lang / Class; flag: ACC_PUBLIC, ACC_FINAL, ACC_NATIVE Underskrift: # 26 // () Ljava / lang / Class; // ...} SourceFile: "Object.java"

3.4. javap-c

Også den javap kommando tillader adskillelse af hele Java-klassen ved hjælp af -c argument:

Kompileret fra "Object.java" offentlig klasse java.lang.Object {public java.lang.Object (); Kode: 0: returner offentlige boolske lig (java.lang.Object); Kode: 0: aload_0 1: aload_1 2: if_acmpne 9 5: iconst_1 6: goto 10 9: iconst_0 10: ireturn beskyttet native java.lang.Object klon () kaster java.lang.CloneNotSupportedException; // ...}

Yderligere, den javap kommando giver os mulighed for at kontrollere systeminfo, konstanter og interne typesignaturer ved hjælp af forskellige argumenter.

Vi kan liste alle argumenter, der understøttes af javap kommando ved hjælp af -Hjælp argument.

Nu hvor vi har set en Java-kommandolinieløsning til visning af bytecode for en klassefil, lad os undersøge et par bytecode-manipulationsbiblioteker.

4. Brug af ASM

ASM er en populær præstationsorienteret Java-bytecode-manipulations- og analyseramme på lavt niveau.

4.1. Opsætning

Lad os først tilføje det seneste asm og asm-util Maven afhængigheder til vores pom.xml:

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

4.2. Se Bytecode

Derefter bruger vi ClassReader og TraceClassVisitor for at se bytekoden til Objekt klasse:

prøv {ClassReader reader = new ClassReader ("java.lang.Object"); StringWriter sw = ny StringWriter (); TraceClassVisitor tcv = ny TraceClassVisitor (ny PrintWriter (System.out)); reader.accept (tcv, 0); } fange (IOException e) {e.printStackTrace (); }

Her bemærker vi, at TraceClassVisitor objekt kræver PrintWriter objekt til at udtrække og producere bytekoden:

// klasse version 52.0 (52) // access flags 0x21 public class java / lang / Object {// kompileret fra: Object.java // access flags 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. Brug af BCEL

Byte Code Engineering Library, populært kendt som Apache Commons BCEL, giver en praktisk måde at oprette / manipulere Java-klassefiler på.

5.1. Maven afhængighed

Lad os som sædvanligt tilføje det seneste bcel Maven afhængighed af vores pom.xml:

 org.apache.bcel bcel 6.5.0 

5.2. Adskil klasse og se Bytecode

Derefter kan vi bruge Datalager klasse til at generere JavaClass objekt:

prøv {JavaClass objectClazz = Repository.lookupClass ("java.lang.Object"); System.out.println (objectClazz.toString ()); } fange (ClassNotFoundException e) {e.printStackTrace (); }

Her har vi brugt toString metode til objectClazz modsætter sig at se bytekode i et kortfattet format:

offentlig klasse java.lang.Object filnavn java.lang.Object udarbejdet fra Object.java compiler version 52.0 adgangsflag 33 konstant pool 78 poster ACC_SUPER flag true Attribut (er): SourceFile: Object.java 14 metoder: public void () private statisk native void registerNatives () public final native Class getClass () [Signature: () Ljava / lang / Class;] public native int hashCode () public boolean equals (Object arg1) protected native Object clone () throw Undtagelser: java.lang .CloneNotSupportedException public String toString () public final native void notify () // ...

Yderligere, den JavaClass klasse giver metoder som getConstantPool, getFieldsog getMethods for at se detaljerne i den adskilte klasse.

assertEquals (objectClazz.getFileName (), "java.lang.Object"); assertEquals (objectClazz.getMethods (). længde, 14); assertTrue (objectClazz.toString (). indeholder ("offentlig klasse java.lang.Object")); 

Tilsvarende sæt* metoder er tilgængelige til bytecode-manipulation.

6. Brug af Javassist

Vi kan også bruge Javassist (Java Programming Assistant) bibliotek, der giver API'er på højt niveau til at se / manipulere Java bytecode.

6.1. Maven afhængighed

Først tilføjer vi det seneste javassist Maven afhængighed af vores pom.xml:

 org.javassist javassist 3.27.0-GA 

6.2. Frembringe ClassFile

Derefter kan vi bruge ClassPool og ClassFile klasser for at generere en Java-klasse:

prøv {ClassPool cp = ClassPool.getDefault (); ClassFile cf = cp.get ("java.lang.Object"). GetClassFile (); cf.write (ny DataOutputStream (ny FileOutputStream ("Object.class"))); } fange (NotFoundException e) {e.printStackTrace (); }

Her har vi brugt skrive metode, der giver os mulighed for at skrive klassefilen ved hjælp af DataOutputStream objekt:

// Kompileret fra Object.java (version 1.8: 52.0, superbit) public class java.lang.Object {// Method descriptor # 19 () V // Stack: 0, Locals: 1 public Object (); 0 retur Linjenumre: [pc: 0, linje: 37] // Metodebeskrivelse nr. 19 () V privat statisk native void registerNatives (); // Metodebeskrivelse nr. 24 () Ljava / lang / Class; // Underskrift: () Ljava / lang / Class; offentlig endelig native java.lang.Class getClass (); // Metodebeskrivelse nr. 28 () I public native int hashCode (); // ...

Også genstanden for ClassFile klasse giver adgang til den konstante pool, felter og metoder:

assertEquals (jf. getName (), "java.lang.Object"); assertEquals (jf. getMethods (). størrelse (), 14);

7. Jclasslib

Derudover kan vi bruge et IDE-baseret plugin til at se bytekoden for en klassefil. Lad os for eksempel udforske jclasslib Bytecode-fremviser plugin tilgængelig til IntelliJ IDEA.

7.1. Installation

Først installerer vi pluginet ved hjælp af dialogboksen Indstillinger / præferencer:

7.2. Se Bytecode på Objekt Klasse

Derefter kan vi vælge "Vis Bytecode With Jclasslib" under menuen View for at se bytekode for den valgte Objekt klasse:

Dernæst åbnes en dialogboks, der viser bytekoden til Objekt klasse:

7.3. Se detaljer

Vi kan også se forskellige detaljer i bytekoden som konstant pool, felter og metoder ved hjælp af Jclasslib-plugin-dialogen:

Tilsvarende har vi Plugin til Bytecode Visualizer for at se bytekoden for en klassefil ved hjælp af formørkelses-IDE.

8. Konklusion

I denne vejledning undersøgte vi måder at se bytekoden til en klassefil i Java.

Først undersøgte vi javap kommando sammen med dens forskellige argumenter. Derefter gik vi gennem et par bytecode-manipulationsbiblioteker, der giver funktionerne til at se og manipulere bytecode.

Til sidst så vi på et IDE-baseret plugin Jclasslib der giver os mulighed for at se bytekode i IntelliJ IDEA.

Som normalt er alle kodeimplementeringer tilgængelige på GitHub.