javap解析class文件
# javap解析class文件
# 概述
通过反编译生成字节码文件,我们可以深入了解java代码的工作机制,除了使用第三方jclasslib工具外,Oracle官方也提供了工具:javap
。
javap
是JDK自带的反解析工具,它的作用就是根据class字节码文件,反解析出当前类对应的code区(字节码码指令)、局部变量表、异常表和代码行偏移量映射表、常量池等信息。通过局部变量表,我们可以查看局部变量表的作用域范围、所在槽位等信息,甚至可以看到槽复用等信息。
# javac -g 说明
解析字节码文件得到的信息中,有些信息如局部变量表、指令和代码行偏移量映射表、常量池中方法的参数名称等等,需要使用javac -g
编译成class文件,之后使用javap
才能反编译输出。
也就说,如果我们直接javac xxx.java
就不会生产对应的局部变量表等信息。默认情况下,Eclipse 或 IDEA,在编译时默认会帮我们生产局部变量表、指令和代码行偏移量映射表等信息的。
# javap的用法
javap的用法格式,其中classes就是你要反编译的class文件。
javap <options> <classes>
在命令行中直接输入javap
或javap -help
可以看到javap
的options
有如下选项:
-help --help -? 输出此用法消息
-version 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。
-v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类 和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编,生产字节码指令
-s 输出内部类型签名
-sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-classpath <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
一般常用的是-v
、-l
、-c
三个选项:
javap -v
: 输出行号、本地变量表信息、反编译生成字节码指令,还会输出当前类用到的常量池等信息。javap -l
: 输出行号和本地变量表信息。javap -c
: 会对当前class字节码进行反编译生成字节码指令。
注意:
-v
相当于-c
、-l
-v
也不会输出私有的字段、方法等信息,所以如果想输出私有的信息,那需要在-v
后面加上-p
才行。
# 使用示例
代码:
public class JavapTest {
private int num;
boolean flag;
protected char gender;
public String info;
public static final int COUNTS = 1;
static{
String url = "www.atguigu.com";
}
{
info = "java";
}
public JavapTest(){
}
private JavapTest(boolean flag){
this.flag = flag;
}
private void methodPrivate(){
}
int getNum(int i){
return num + i;
}
protected char showGender(){
return gender;
}
public void showInfo(){
int i = 10;
System.out.println(info + i);
}
}
执行javap指令
:
javap -v -p JavapTest.class
字节码文件:
Classfile /Users/wenwl/Projects/JVMDemo1/out/production/chapter01/com/atguigu/java1/JavapTest.class // 字节码文件所属的路径
Last modified 2022年3月11日; size 1358 bytes // 最后修改时间,字节码文件的大小
MD5 checksum 526b4a845e4d98180438e4c5781b7e88 // MD5散列值
Compiled from "JavapTest.java" // 源文件的名称
public class io.renren.JavapTest
minor version: 0 // 副版本
major version: 52 // 主版本
flags: ACC_PUBLIC, ACC_SUPER // 访问标识
*************************** 常量池 ********************************
Constant pool:
#1 = Methodref #16.#48 // java/lang/Object."<init>":()V
#2 = String #49 // java
#3 = Fieldref #15.#50 // io/renren/JavapTest.info:Ljava/lang/String;
#4 = Fieldref #15.#51 // io/renren/JavapTest.flag:Z
#5 = Fieldref #15.#52 // io/renren/JavapTest.num:I
#6 = Fieldref #15.#53 // io/renren/JavapTest.gender:C
#7 = Fieldref #54.#55 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Class #56 // java/lang/StringBuilder
#9 = Methodref #8.#48 // java/lang/StringBuilder."<init>":()V
#10 = Methodref #8.#57 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#11 = Methodref #8.#58 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#12 = Methodref #8.#59 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#13 = Methodref #60.#61 // java/io/PrintStream.println:(Ljava/lang/String;)V
#14 = String #62 // www.atguigu.com
#15 = Class #63 // io/renren/JavapTest
#16 = Class #64 // java/lang/Object
#17 = Utf8 num
#18 = Utf8 I
#19 = Utf8 flag
#20 = Utf8 Z
#21 = Utf8 gender
#22 = Utf8 C
#23 = Utf8 info
#24 = Utf8 Ljava/lang/String;
#25 = Utf8 COUNTS
#26 = Utf8 ConstantValue
#27 = Integer 1
#28 = Utf8 <init>
#29 = Utf8 ()V
#30 = Utf8 Code
#31 = Utf8 LineNumberTable
#32 = Utf8 LocalVariableTable
#33 = Utf8 this
#34 = Utf8 Lio/renren/JavapTest;
#35 = Utf8 (Z)V
#36 = Utf8 falg
#37 = Utf8 MethodParameters
#38 = Utf8 methodPrivate
#39 = Utf8 getNum
#40 = Utf8 (I)I
#41 = Utf8 i
#42 = Utf8 showGender
#43 = Utf8 ()C
#44 = Utf8 showInfo
#45 = Utf8 <clinit>
#46 = Utf8 SourceFile
#47 = Utf8 JavapTest.java
#48 = NameAndType #28:#29 // "<init>":()V
#49 = Utf8 java
#50 = NameAndType #23:#24 // info:Ljava/lang/String;
#51 = NameAndType #19:#20 // flag:Z
#52 = NameAndType #17:#18 // num:I
#53 = NameAndType #21:#22 // gender:C
#54 = Class #65 // java/lang/System
#55 = NameAndType #66:#67 // out:Ljava/io/PrintStream;
#56 = Utf8 java/lang/StringBuilder
#57 = NameAndType #68:#69 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#58 = NameAndType #68:#70 // append:(I)Ljava/lang/StringBuilder;
#59 = NameAndType #71:#72 // toString:()Ljava/lang/String;
#60 = Class #73 // java/io/PrintStream
#61 = NameAndType #74:#75 // println:(Ljava/lang/String;)V
#62 = Utf8 www.atguigu.com
#63 = Utf8 io/renren/JavapTest
#64 = Utf8 java/lang/Object
#65 = Utf8 java/lang/System
#66 = Utf8 out
#67 = Utf8 Ljava/io/PrintStream;
#68 = Utf8 append
#69 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#70 = Utf8 (I)Ljava/lang/StringBuilder;
#71 = Utf8 toString
#72 = Utf8 ()Ljava/lang/String;
#73 = Utf8 java/io/PrintStream
#74 = Utf8 println
#75 = Utf8 (Ljava/lang/String;)V
****************************** 字段表集合的信息 **************************************
{
private int num; // 字段名
descriptor: I // 字段表集合的信息
flags: ACC_PRIVATE // 字段的访问标识
boolean flag;
descriptor: Z
flags:
protected char gender;
descriptor: C
flags: ACC_PROTECTED
public java.lang.String info;
descriptor: Ljava/lang/String;
flags: ACC_PUBLIC
public static final int COUNTS;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: int 1 // 常量字段的属性:ConstantValue
****************************** 方法表集合的信息 **************************************
public io.renren.JavapTest(); // 无参构造器方法信息
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String java
7: putfield #3 // Field info:Ljava/lang/String;
10: return
LineNumberTable:
line 16: 0
line 14: 4
line 18: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Lio/renren/JavapTest;
private io.renren.JavapTest(boolean); // 单个参数构造器方法信息
descriptor: (Z)V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String java
7: putfield #3 // Field info:Ljava/lang/String;
10: aload_0
11: aload_0
12: getfield #4 // Field flag:Z
15: putfield #4 // Field flag:Z
18: return
LineNumberTable:
line 19: 0
line 14: 4
line 20: 10
line 21: 18
LocalVariableTable:
Start Length Slot Name Signature
0 19 0 this Lio/renren/JavapTest;
0 19 1 falg Z
MethodParameters:
Name Flags
falg
private void methodPrivate();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 24: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Lio/renren/JavapTest;
int getNum(int);
descriptor: (I)I
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: getfield #5 // Field num:I
4: iload_1
5: iadd
6: ireturn
LineNumberTable:
line 26: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lio/renren/JavapTest;
0 7 1 i I
MethodParameters:
Name Flags
i
protected char showGender();
descriptor: ()C
flags: ACC_PROTECTED
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #6 // Field gender:C
4: ireturn
LineNumberTable:
line 29: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lio/renren/JavapTest;
public void showInfo();
descriptor: ()V // 方法的描述符:方法的形参列表、返回值类型
flags: ACC_PUBLIC // 方法的访问标识
Code: // 方法的Code属性
stack=3, locals=2, args_size=1 // stack:操作数栈的最大深度 locals:局部变量表的长度 args_size:方法接受参数的个数
// 偏移量 操作码 操作数
0: bipush 100
2: istore_1
3: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
6: new #8 // class java/lang/StringBuilder
9: dup
10: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
13: aload_0
14: getfield #3 // Field info:Ljava/lang/String;
17: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: iload_1
21: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
24: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
// 行号表:指明字节码指令的偏移量与java源代码中代码的行号的一一对应关系
LineNumberTable:
line 32: 0
line 33: 3
line 34: 30
// 局部变量表:描述内部局部变量的相关信息
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 this Lio/renren/JavapTest;
3 28 1 i I
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=1, args_size=0
0: ldc #14 // String www.atguigu.com
2: astore_0
3: return
LineNumberTable:
line 11: 0
line 12: 3
LocalVariableTable:
Start Length Slot Name Signature
}
SourceFile: "JavapTest.java" // 附加属性:指明当前字节码文件对应的源程序文件名
# 总结
javap
命令可以查看一个Java类反汇编得到的Class文件版本号、常量池、访问标识、变量表、指令代码行号表等信息,不显示类索引、父类索引、接口索引集合、<clinit>()、<init>()等结构。- 通过对上述例子反汇编文件简单分析发现,一个方法对执行通常会涉及下面几块内存操作:
- java栈:局部变量表、操作数栈
- java堆:通过对象的地址引用去操作
- 常量池
- 其他区域则没有显示出来。
- 平常我们比较关注的是java类中每个方法的反汇编中的指令操作过程,这些指令都是顺序执行的,可以参考官方文档 (opens new window),查看每个指令的含义。
上次更新: 5/28/2023, 10:57:53 PM