Перейти к основному содержимому

Просмотр байт-кода файла класса в Java

· 7 мин. чтения

1. Обзор

Анализ байт-кода является обычной практикой среди разработчиков Java по многим причинам, таким как поиск проблем с кодом, профилирование кода и поиск классов с определенными аннотациями.

В этой статье мы рассмотрим способы просмотра байт-кода файла класса в Java.

2. Что такое байт-код?

Байт-код — это промежуточное представление программы Java, позволяющее JVM преобразовывать программу в инструкции по сборке машинного уровня .

Когда Java-программа компилируется, байт-код генерируется в виде файла .class . Этот файл .class содержит невыполнимые инструкции и требует интерпретации JVM.

3. Использование javap

Командная строка Java поставляется с инструментом javap , который отображает информацию о полях, конструкторах и методах файла класса.

На основе используемых параметров он может разобрать класс и показать инструкции, составляющие байт-код Java.

3.1. джавап

Давайте воспользуемся командой javap для просмотра байт-кода самого распространенного класса Object :

$ javap java.lang.Object

Вывод команды покажет минимальную конструкцию класса Object :

public class java.lang.Object {
public java.lang.Object();
public final native java.lang.Class<?> getClass();
public native int hashCode();
public boolean equals(java.lang.Object);
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
public java.lang.String toString();
public final native void notify();
public final native void notifyAll();
public final native void wait(long) throws java.lang.InterruptedException;
public final void wait(long, int) throws java.lang.InterruptedException;
public final void wait() throws java.lang.InterruptedException;
protected void finalize() throws java.lang.Throwable;
static {};
}

По умолчанию вывод байт-кода не будет содержать полей/методов с приватным модификатором доступа .

3.2. javap- p

Чтобы просмотреть все классы и члены, мы можем использовать аргумент -p :

public class java.lang.Object {
public java.lang.Object();
private static native void registerNatives();
public final native java.lang.Class<?> getClass();
public native int hashCode();
public boolean equals(java.lang.Object);
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
// ...
}

Здесь мы можем наблюдать, как частный метод registerNatives также показан в байт-коде класса Object .

3.3. javap -v

Точно так же мы можем использовать аргумент -v для просмотра подробной информации, такой как размер стека и аргументы для методов класса Object :

Classfile jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/Object.class
Last modified Mar 15, 2017; size 1497 bytes
MD5 checksum 5916745820b5eb3e5647da3b6cc6ef65
Compiled from "Object.java"
public class java.lang.Object
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #49 // java/lang/StringBuilder
// ...
{
public java.lang.Object();
descriptor: ()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();
descriptor: ()Ljava/lang/Class;
flags: ACC_PUBLIC, ACC_FINAL, ACC_NATIVE
Signature: #26 // ()Ljava/lang/Class<*>;

// ...
}
SourceFile: "Object.java"

3.4. javap -c

Кроме того, команда javap позволяет дизассемблировать весь класс Java с помощью аргумента -c :

Compiled from "Object.java"
public class java.lang.Object {
public java.lang.Object();
Code:
0: return
public boolean equals(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
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
// ...
}

Кроме того, команда javap позволяет нам проверять системную информацию, константы и сигнатуры внутренних типов, используя различные аргументы.

Мы можем перечислить все аргументы, поддерживаемые командой javap , используя аргумент -help .

Теперь, когда мы рассмотрели решение командной строки Java для просмотра байт-кода файла класса, давайте рассмотрим несколько библиотек для работы с байт-кодом.

4. Использование АСМ

ASM — это популярная ориентированная на производительность низкоуровневая среда для обработки и анализа байт-кода Java.

4.1. Настраивать

Во-первых, давайте добавим последние зависимости asm и asm-util Maven в наш pom.xml :

<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>8.0.1</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>8.0.1</version>
</dependency>

4.2. Посмотреть байт-код

Затем мы будем использовать ClassReader и TraceClassVisitor для просмотра байт-кода класса Object :

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

Здесь мы отметим, что объекту TraceClassVisitor требуется объект PrintWriter для извлечения и создания байт-кода:

// class version 52.0 (52)
// access flags 0x21
public class java/lang/Object {

// compiled from: Object.java

// access flags 0x1
public <init>()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. Использование БКЭЛ

Библиотека разработки байт-кода, широко известная как Apache Commons BCEL , предоставляет удобный способ создания файлов классов Java и управления ими.

5.1. Зависимость от Maven

Как обычно, добавим последнюю зависимость bcel Maven в наш pom.xml :

<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.5.0</version>
</dependency>

5.2. Дизассемблировать класс и просмотреть байт-код

Затем мы можем использовать класс Repository для создания объекта JavaClass :

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

Здесь мы использовали метод toString для объекта objectClazz , чтобы увидеть байт-код в сжатом формате: ``

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 methods:
public void <init>()
private static 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()
throws Exceptions: java.lang.CloneNotSupportedException
public String toString()
public final native void notify()

// ...

Кроме того, класс JavaClass предоставляет такие методы, как getConstantPool , getFields и getMethods , для просмотра сведений о дизассемблированном классе .

assertEquals(objectClazz.getFileName(), "java.lang.Object");
assertEquals(objectClazz.getMethods().length, 14);
assertTrue(objectClazz.toString().contains("public class java.lang.Object"));

Точно так же методы set* доступны для манипулирования байт-кодом.

6. Использование Javassist

Кроме того, мы можем использовать библиотеку Javassist ( помощник по программированию на Java) , которая предоставляет API высокого уровня для просмотра/манипулирования байт-кодом Java.

6.1. Зависимость от Maven

Во-первых, мы добавим последнюю зависимость javassist Maven в наш pom.xml :

<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>

6.2. Создать ClassFile

Затем мы можем использовать классы ClassPool и ClassFile для создания класса Java :

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

Здесь мы использовали метод записи , который позволяет нам записать файл класса с использованием объекта DataOutputStream :

// Compiled from Object.java (version 1.8 : 52.0, super bit)
public class java.lang.Object {

// Method descriptor #19 ()V
// Stack: 0, Locals: 1
public Object();
0 return
Line numbers:
[pc: 0, line: 37]

// Method descriptor #19 ()V
private static native void registerNatives();

// Method descriptor #24 ()Ljava/lang/Class;
// Signature: ()Ljava/lang/Class<*>;
public final native java.lang.Class getClass();

// Method descriptor #28 ()I
public native int hashCode();

// ...

Также объект класса ClassFile предоставляет доступ к пулу констант, полям и методам:

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

7. Jclasslib

Кроме того, мы можем использовать плагин на основе IDE для просмотра байт-кода файла класса. Например, давайте рассмотрим подключаемый модуль jclasslib для просмотра байт -кода, доступный для IntelliJ IDEA.

7.1. Монтаж

Во-первых, мы установим плагин с помощью диалогового окна Settings/Preferences:

./615149f08fd520bd35cc93d8a396a0ef.png

7.2. Просмотр байт-кода класса объекта

Затем мы можем выбрать опцию «Показать байт-код с помощью Jclasslib» в меню «Вид», чтобы просмотреть байт-код выбранного класса объектов :

./79b5823bbd6e435de5987d09662c1303.png

Далее откроется диалоговое окно для отображения байт-кода класса Object :

./3d1edfbabda7d2367f694f03a4d24c8e.png

7.3. Посмотреть детали

Кроме того, мы можем увидеть различные детали байт-кода, такие как постоянный пул, поля и методы, используя диалоговое окно плагина Jclasslib:

./87e2470ebe7191897ca023ede296f0bb.png

Точно так же у нас есть плагин Bytecode Visualizer для просмотра байт-кода файла класса с помощью Eclipse IDE.

8. Заключение

В этом руководстве мы рассмотрели способы просмотра байт-кода файла класса в Java.

Сначала мы рассмотрели команду javap вместе с ее различными аргументами. Затем мы рассмотрели несколько библиотек для работы с байт-кодом, которые предоставляют функции для просмотра и управления байт-кодом.

Наконец, мы рассмотрели плагин Jclasslib на основе IDE , который позволяет нам просматривать байт-код в IntelliJ IDEA.

Как обычно, все реализации кода доступны на GitHub .