Рубрики
Без рубрики

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

Изучите способы просмотра байт-кода файла класса в Java

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