Vis Bytecode for en klassefil i Java

1. Oversikt

Bytecode-analyse er en vanlig praksis blant Java-utviklere av mange grunner, som å finne problemer med kode, kodeprofilering og søke på klasser med spesifikke merknader.

I denne artikkelen vil vi utforske måter å vise bytekoden til en klassefil i Java.

2. Hva er Bytecode?

Bytecode er den mellomliggende representasjonen av et Java-program, slik at en JVM kan oversette et program til monteringsinstruksjoner på maskinnivå.

Når et Java-program blir kompilert, genereres bytekode i form av en .klasse fil. Dette .klasse filen inneholder ikke-kjørbare instruksjoner og er avhengig av at en JVM skal tolkes.

3. Bruke javap

Java-kommandolinjen kommer med javap verktøy som viser informasjon om felt, konstruktører og metoder for en klassefil.

Basert på alternativene som brukes, kan den demontere en klasse og vise instruksjonene som inneholder Java bytecode.

3.1. javap

La oss bruke javap kommando for å vise bytekoden til det vanligste Gjenstand klasse:

$ javap java.lang.Object

Utgangen av kommandoen vil vise den minste konstruksjon av Gjenstand klasse:

offentlig klasse java.lang.Object {offentlig java.lang.Object (); offentlig endelig innfødt java.lang.Class getClass (); offentlig innfødt int hashCode (); offentlig boolsk lik (java.lang.Object); beskyttet native java.lang.Object clone () kaster java.lang.CloneNotSupportedException; offentlig java.lang.String toString (); offentlig endelig native void notify (); offentlig endelig native void notifyAll (); offentlig endelig innfødt ugyldig vent (lang) kaster java.lang.InterruptedException; offentlig endelig ugyldig vent (lang, int) kaster java.lang.InterruptedException; offentlig endelig ugyldig vent () kaster java.lang.InterruptedException; beskyttet tomrom finalize () kaster java.lang.Throwable; statisk {}; }

Som standard vil ikke bytekodeutgangen inneholde felt / metoder med en privat tilgangsmodifikator.

3.2. javap-p

For å se alle klasser og medlemmer kan vi bruke -p argument:

offentlig klasse java.lang.Object {offentlig java.lang.Object (); private statiske native void registerNatives (); offentlig endelig innfødt java.lang.Class getClass (); offentlig innfødt int hashCode (); offentlig boolsk lik (java.lang.Object); beskyttet native java.lang.Object clone () kaster java.lang.CloneNotSupportedException; // ...}

Her kan vi observere en privat metode registerNatives er også vist i bytekoden til Gjenstand klasse.

3.3. javap-v

På samme måte kan vi bruke -v argument for å vise utførlig informasjon som stabelstørrelse og argumenter for metoder for Gjenstand klasse:

Classfile jar: file: /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar! /Java/lang/Object.class Sist endret 15. mars 2017; størrelse 1497 byte MD5 kontrollsum 5916745820b5eb3e5647da3b6cc6ef65 Kompilert fra "Object.java" offentlig klasse java.lang.Object mindre versjon: 0 hovedversjon: 52 flagg: ACC_PUBLIC, ACC_SUPER Konstant basseng: # 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; flagg: ACC_PUBLIC, ACC_FINAL, ACC_NATIVE Signatur: # 26 // () Ljava / lang / Class; // ...} SourceFile: "Object.java"

3.4. javap-c

Også, den javap kommandoen tillater demontering av hele Java-klassen ved hjelp av -c argument:

Kompilert fra "Object.java" offentlig klasse java.lang.Object {public java.lang.Object (); Kode: 0: returner offentlig boolsk lik (java.lang.Object); Kode: 0: aload_0 1: aload_1 2: if_acmpne 9 5: iconst_1 6: gå til 10 9: iconst_0 10: ireturbeskyttet innfødt java.lang.Object clone () kaster java.lang.CloneNotSupportedException; // ...}

Videre, den javap kommandoen lar oss sjekke systeminformasjon, konstanter og interne typesignaturer ved hjelp av forskjellige argumenter.

Vi kan liste opp alle argumenter som støttes av javap kommandoen ved å bruke -hjelp argument.

Nå som vi har sett en Java-kommandolinjeløsning for visning av bytecode for en klassefil, la oss undersøke noen få bytecode-manipulasjonsbiblioteker.

4. Bruke ASM

ASM er et populært ytelsesorientert, lavt nivå Java bytecode-manipulerings- og analyserammeverk.

4.1. Oppsett

La oss først legge til det siste asm og asm-util Maven avhengigheter til vår pom.xml:

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

4.2. Vis Bytecode

Så bruker vi ClassReader og TraceClassVisitor for å se bytekoden til Gjenstand 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 vil vi merke oss at TraceClassVisitor objektet krever PrintWriter objekt for å trekke ut og produsere bytekoden:

// klasseversjon 52.0 (52) // tilgangsflagg 0x21 offentlig klasse java / lang / Object {// kompilert fra: Object.java // tilgangsflagg 0x1 offentlig () 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. Bruke BCEL

Byte Code Engineering Library, populært kjent som Apache Commons BCEL, gir en praktisk måte å lage / manipulere Java-klassefiler på.

5.1. Maven avhengighet

Som vanlig, la oss legge til det siste bcel Maven avhengighet til vår pom.xml:

 org.apache.bcel bcel 6.5.0 

5.2. Demonter klasse og vis Bytecode

Deretter kan vi bruke Oppbevaringssted klasse for å generere JavaClass gjenstand:

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

Her har vi brukt toString metoden på objectClazz motsette seg å se bytekode i et kortfattet format:

public class java.lang.Object file name java.lang.Object compiled from Object.java compiler version 52.0 access flags 33 constant pool 78 entries ACC_SUPER flag true Attribute (s): 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 Exceptions: java.lang .CloneNotSupportedException public String toString () public final native void notify () // ...

Videre, den JavaClass klasse gir metoder som getConstantPool, getFields, og getMethods for å se detaljene i den demonterte klassen.

assertEquals (objectClazz.getFileName (), "java.lang.Object"); assertEquals (objectClazz.getMethods (). lengde, 14); assertTrue (objectClazz.toString (). inneholder ("offentlig klasse java.lang.Object")); 

På samme måte, sett* metoder er tilgjengelige for manipulering av bytecode.

6. Bruke Javassist

Vi kan også bruke Javassist (Java Programming Assistant) bibliotek som gir APIer på høyt nivå for å vise / manipulere Java bytecode.

6.1. Maven avhengighet

Først legger vi til det siste javassist Maven avhengighet til vår pom.xml:

 org.javassist javassist 3.27.0-GA 

6.2. generere ClassFile

Deretter kan vi bruke ClassPool og ClassFile klasser for å 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 brukt skrive metode, som lar oss skrive klassefilen ved hjelp av DataOutputStream gjenstand:

// Kompilert fra Object.java (versjon 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 innfødt java.lang.Class getClass (); // Metodebeskrivelse nr. 28 () I offentlig innfødt int hashCode (); // ...

Også objektet til ClassFile klasse gir tilgang til konstant basseng, felt og metoder:

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

7. Jclasslib

I tillegg kan vi bruke et IDE-basert plugin for å vise bytekoden til en klassefil. La oss for eksempel utforske jclasslib Bytecode-seer plugin tilgjengelig for IntelliJ IDEA.

7.1. Installasjon

Først installerer vi pluginet ved hjelp av dialogboksen Innstillinger / innstillinger:

7.2. Vis Bytecode av Gjenstand Klasse

Deretter kan vi velge "Vis Bytecode With Jclasslib" under Vis-menyen for å vise bykode for den valgte Gjenstand klasse:

Deretter åpnes en dialogboks for å vise bytekoden til Gjenstand klasse:

7.3. Vis detaljer

Vi kan også se forskjellige detaljer om bytekoden som konstant basseng, felt og metoder ved hjelp av Jclasslib-plugin-dialogen:

Tilsvarende har vi Bytecode Visualizer Plugin for å vise bytekoden til en klassefil ved hjelp av formørkelsen IDE.

8. Konklusjon

I denne opplæringen undersøkte vi måter å vise bytekoden til en klassefil i Java.

Først undersøkte vi javap kommando sammen med dens forskjellige argumenter. Deretter gikk vi gjennom noen bytecode-manipulasjonsbiblioteker som gir funksjonene for å se og manipulere bytecode.

Sist så vi på et IDE-basert plugin Jclasslib som lar oss se bytekode i IntelliJ IDEA.

Som vanlig er alle kodeimplementeringene tilgjengelige på GitHub.