1. Overview
1.概述
Bytecode analysis is a common practice among Java developers for many reasons, like finding problems with code, code profiling, and searching classes with specific annotations.
字节码分析在Java开发者中是一种常见的做法,原因有很多,比如发现代码中的问题、代码剖析,以及搜索具有特定注释的类。
In this article, we’ll explore ways to view the bytecode of a class file in Java.
在这篇文章中,我们将探讨在Java中查看类文件的字节码的方法。
2. What Is the Bytecode?
2.什么是字节码?
Bytecode is the intermediate representation of a Java program, allowing a JVM to translate a program into machine-level assembly instructions.
字节码是Java程序的中间表示,允许JVM将程序翻译成机器级汇编指令。
When a Java program is compiled, bytecode is generated in the form of a .class file. This .class file contains non-runnable instructions and relies on a JVM to be interpreted.
当一个Java程序被编译时,字节码以.class文件的形式生成。这个.class文件包含不可运行的指令,并依赖JVM进行解释。
3. Using javap
3.使用javap
The Java command-line comes with the javap tool that displays information about the fields, constructors, and methods of a class file.
Java命令行自带的javap工具可以显示一个类文件的字段、构造函数和方法的信息。
Based on the options used, it can disassemble a class and show the instructions that comprise the Java bytecode.
根据所使用的选项,它可以分解一个类并显示构成Java字节码的指令。
3.1. javap
3.1. Java
Let’s use the javap command to view the bytecode of the most-common Object class:
让我们使用javap命令来查看最常见的Object类的字节码。
$ javap java.lang.Object
The output of the command will show the bare-minimum construct of the Object class:
命令的输出将显示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 {};
}
By default, the bytecode output will not contain fields/methods with a private access modifier.
默认情况下,字节码输出将不包含带有a private access modifier的字段/方法。
3.2. javap -p
3.2. ahead -p
To view all classes and members, we can use the -p argument:
要查看所有的类和成员,我们可以使用-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;
// ...
}
Here, we can observe a private method registerNatives is also shown in the bytecode of the Object class.
在这里,我们可以观察到一个private方法registerNatives也显示在Object类的字节码中。
3.3. javap -v
3.3.意识到-v
Similarly, we can use the -v argument to view verbose information like stack size and arguments for methods of the Object class:
同样,我们可以使用-v参数来查看堆栈大小和Objectclass方法的参数等粗略的信息。
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
3.4. JAVAP C
Also, the javap command allows disassembling the whole Java class by using the -c argument:
另外,javap命令允许通过使用-c参数来反汇编整个Java类。
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;
// ...
}
Further, the javap command allows us to check the system info, constants, and internal type signatures using various arguments.
此外,javap命令允许我们使用各种参数检查系统信息、常量和内部类型签名。
We can list all arguments supported by the javap command by using the -help argument.
我们可以通过使用-help参数列出javap命令支持的所有参数。
Now that we’ve seen a Java command-line solution for viewing the bytecode of a class file, let’s examine a few bytecode-manipulation libraries.
现在我们已经看到了一个查看类文件字节码的Java命令行解决方案,让我们来看看一些字节码处理库。
4. Using ASM
4.使用ASM
ASM is a popular performance-oriented, low-level Java bytecode manipulation and analysis framework.
ASM是一个流行的面向性能的、低级别的Java字节码操作和分析框架。
4.1. Setup
4.1.设置
First, let’s add the latest asm and asm-util Maven dependencies to our pom.xml:
首先,让我们把最新的asm和asm-utilMaven依赖项加入我们的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. View Bytecode
4.2.查看字节码
Then, we’ll use the ClassReader and TraceClassVisitor to view the bytecode of the Object class:
然后,我们将使用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();
}
Here, we’ll note that the TraceClassVisitor object requires the PrintWriter object to extract and produce the bytecode:
在这里,我们将注意到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. Using BCEL
5.使用BCEL
The Byte Code Engineering Library, popularly known as Apache Commons BCEL, provides a convenient way to create/manipulate Java class files.
字节代码工程库,俗称Apache Commons BCEL,提供了一种创建/操纵Java类文件的便捷方式。
5.1. Maven Dependency
5.1.Maven的依赖性
As usual, let’s add the latest bcel Maven dependency to our pom.xml:
像往常一样,让我们把最新的bcelMaven依赖性添加到我们的pom.xml。
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.5.0</version>
</dependency>
5.2. Disassemble Class and View Bytecode
5.2.反汇编类和查看字节码
Then, we can use the Repository class to generate the JavaClass object:
然后,我们可以使用Repository类来生成JavaClass对象。
try {
JavaClass objectClazz = Repository.lookupClass("java.lang.Object");
System.out.println(objectClazz.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Here, we’ve used the toString method on the objectClazz object to see bytecode in a concise format:
在这里,我们在objectClazz对象上使用了toString方法,以简明的格式看到字节码:
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()
// ...
Further, the JavaClass class provides methods like getConstantPool, getFields, and getMethods to view the details of the disassembled class.
此外,JavaClass类提供了诸如getConstantPool、getFields和getMethods等方法,以查看被拆解类的细节。
assertEquals(objectClazz.getFileName(), "java.lang.Object");
assertEquals(objectClazz.getMethods().length, 14);
assertTrue(objectClazz.toString().contains("public class java.lang.Object"));
Similarly, set* methods are available for bytecode manipulation.
同样地,set*方法也可用于字节码操作。
6. Using Javassist
6.使用Javassist
Also, we can use the Javassist (Java Programming Assistant) library that provides high-level APIs to view/manipulate Java bytecode.
此外,我们还可以使用Javassist(Java编程助手)库,该库提供了查看/操纵Java字节码的高级API。
6.1. Maven Dependency
6.1.Maven的依赖性
First, we’ll add the latest javassist Maven dependency to our pom.xml:
首先,我们要把最新的javassistMaven依赖性添加到我们的pom.xml。
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
6.2. Generate ClassFile
6.2.生成类文件
Then, we can use the ClassPool and ClassFile classes to generate a Java class:
然后,我们可以使用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();
}
Here, we’ve used the write method, which allows us to write the class file using the DataOutputStream object:
在这里,我们使用了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();
// ...
Also, the object of the ClassFile class provides access to the constant pool, fields, and methods:
另外,ClassFile类的对象提供了对常量池、字段和方法的访问。
assertEquals(cf.getName(), "java.lang.Object");
assertEquals(cf.getMethods().size(), 14);
7. Jclasslib
7、jclasslib
Additionally, we can use an IDE based plugin to view the bytecode of a class file. For instance, let’s explore the jclasslib Bytecode viewer plugin available for IntelliJ IDEA.
此外,我们可以使用基于IDE的插件来查看类文件的字节码。例如,让我们探讨一下IntelliJ IDEA的jclasslib字节码查看器插件。
7.1. Installation
7.1.安装
First, we’ll install the plugin using the Settings/Preferences dialog:
首先,我们将使用 “设置/首选项 “对话框安装该插件。
7.2. View Bytecode of the Object Class
7.2.查看Object类的字节码
Then, we can choose “Show Bytecode With Jclasslib” option under the View menu to view bytecode of the selected Object class:
然后,我们可以在视图菜单下选择 “用Jclasslib显示字节码 “选项,以查看所选Object类的字节码。
Next, a dialog will open to show the bytecode of the Object class:
接下来,将打开一个对话框,显示Objectclass的字节码。
7.3. View Details
7.3.查看详情
Also, we can see various details of the bytecode like constant pool, fields, and methods using the Jclasslib plugin dialog:
另外,我们可以使用Jclasslib插件对话框看到字节码的各种细节,如常量池、字段和方法。
Similarly, we have the Bytecode Visualizer Plugin to view the bytecode of a class file using the Eclipse IDE.
同样地,我们有Bytecode Visualizer Plugin,可以使用Eclipse IDE查看类文件的字节码。
8. Conclusion
8.结语
In this tutorial, we explored ways to view the bytecode of a class file in Java.
在本教程中,我们探讨了在Java中查看类文件字节码的方法。
First, we examined the javap command along with its various arguments. Then, we went through a few bytecode manipulation libraries that provide the features to view and manipulate the bytecode.
首先,我们研究了javap命令及其各种参数。然后,我们浏览了一些字节码操作库,它们提供了查看和操作字节码的功能。
Last, we looked into an IDE based plugin Jclasslib that allows us to view bytecode in IntelliJ IDEA.
最后,我们研究了一个基于IDE的插件Jclasslib,允许我们在IntelliJ IDEA中查看字节码。
As usual, all the code implementations are available over on GitHub.
像往常一样,所有的代码实现都可以在GitHub上找到,。