Автор оригинала: Anshul Bansal.
1. Обзор
Анализ байт-кода является обычной практикой среди разработчиков Java по многим причинам, таким как поиск проблем с кодом, профилирование кода и поиск классов с конкретными аннотациями.
В этой статье мы рассмотрим способы просмотра байт-кода файла класса в Java.
2. Что такое байт-Код?
Байт-код-это промежуточное представление программы Java, позволяющее JVM переводить программу в инструкции по сборке на машинном уровне.
При компиляции программы Java байт-код генерируется в виде файла .class . Этот файл .class содержит неисполняемые инструкции и зависит от интерпретируемой JVM.
3. Использование java
Командная строка Java поставляется с инструментом javap , который отображает информацию о полях, конструкторах и методах файла класса.
Основываясь на используемых параметрах, он может разобрать класс и показать инструкции, которые содержат байт-код Java.
3.1. javap
Давайте используем команду 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;
// ...
}Здесь мы можем наблюдать, что private метод registerNatives также отображается в байт-коде класса Object .
3.3. javap -v
Аналогично, мы можем использовать аргумент -v для просмотра подробной информации, такой как размер стека и аргументы для методов Объекта класса :
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
ASM-это популярная ориентированная на производительность низкоуровневая платформа для обработки и анализа байт-кода Java.
4.1. Настройка
Во-первых, давайте добавим последние зависимости asm и asm-util Maven в ваш pom.xml :
org.ow2.asm asm 8.0.1 org.ow2.asm asm-util 8.0.1
4.2. Просмотр байт-кода
Затем мы будем использовать ClassReader и TraceClassVisitor для просмотра байт-кода объекта класса:
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 ()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
Инженерная библиотека байт-кода, известная в народе как Apache Commons BCEL , предоставляет удобный способ создания/управления файлами классов Java.
5.1. Зависимость Maven
Как обычно, давайте добавим последнюю зависимость bcel Maven в ваш pom.xml :
org.apache.bcel bcel 6.5.0
5.2. Разберите класс и просмотрите байт-код
Затем мы можем использовать класс Repository для создания объекта Java-класса :
try {
JavaClass objectClazz = Repository.lookupClass("java.lang.Object");
System.out.println(objectClazz.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}Здесь мы использовали метод toString для объекта object Clazz , чтобы увидеть байт-код в сжатом формате:
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 ()
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()
// ... Кроме того, класс Java предоставляет такие методы, как 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 :
org.javassist javassist 3.27.0-GA
6.2. Создание файла класса
Затем мы можем использовать Пул классов и Файл класса классы для создания класса 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();
}Здесь мы использовали метод write , который позволяет нам записать файл класса с помощью объекта 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();
// ...Кроме того, объект файла Class class предоставляет доступ к пулу констант, полям и методам:
assertEquals(cf.getName(), "java.lang.Object"); assertEquals(cf.getMethods().size(), 14);
7. Jclasslib
Кроме того, мы можем использовать плагин на основе IDE для просмотра байт-кода файла класса. Например, давайте рассмотрим плагин jclasslib Bytecode viewer , доступный для IntelliJ IDEA.
7.1. Установка
Во-первых, мы установим плагин, используя диалоговое окно Настройки/Настройки:
7.2. Просмотр байт-кода класса объектов
Затем мы можем выбрать опцию “Показать байт-код с помощью Jclasslib” в меню Вид, чтобы просмотреть байт-код выбранного Объекта класса:
Затем откроется диалоговое окно, в котором будет показан байт-код класса Object :
7.3. Просмотр сведений
Кроме того, мы можем увидеть различные детали байт-кода, такие как постоянный пул, поля и методы, используя диалоговое окно плагина Jclasslib:
Аналогично, у нас есть плагин Bytecode Visualizer для просмотра байт-кода файла класса с помощью среды разработки Eclipse.
8. Заключение
В этом уроке мы рассмотрели способы просмотра байт-кода файла класса в Java.
Сначала мы рассмотрели команду javap вместе с ее различными аргументами. Затем мы рассмотрели несколько библиотек управления байт-кодом, которые предоставляют функции для просмотра и управления байт-кодом.
Наконец, мы рассмотрели плагин на основе IDE Jclasslib , который позволяет нам просматривать байт-код в IntelliJ IDEA.
Как обычно, все реализации кода доступны на GitHub .