Автор оригинала: 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 .