javap
通过反编译生成的字节码文件,我们可以深入的了解java代码的工作机制。但是,自己分析类文件结构太麻烦了!除了使用第三方的jclasslib工具之外,oracle官方也提供了工具:javap。
javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区 (字节码指令)、局部变量表、异常表和代码行偏移量映射表、常量池等信息。
通过局部变量表,我们可以查看局部变量的作用域范围、所在槽位等信息,甚至可以看到槽位复用等信息。
javac -g操作
解析字节码文件得到的信息中,有些信息(如局部变量表、指令和代码行偏移量映射表、常量池中方法的参数名称等等)需要在使用javac编译成class文件时,指定参数才能输出。
比如,你直接javac xx.java,就不会在生成对应的局部变量表等信息,如果你使用javac -g xx.java就可以生成所有相关信息了。如果你使用的eclipse或IDEA,则默认情况下, eclipse、IDEA在编译时会帮你生成局部变量表、指令和代码行偏移量映射表等信息的。
javap的用法
一般常用的是-v -l -c三个选项。
- javap -l 会输出行号和本地变量表信息。
- javap -c 会对当前class字节码进行反编译生成汇编代码。
- javap -v classxx除了包含一c内容外,还会输出行号、局部变量表信息、常量池等信息。
以如下代码为例
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 -public JavapTest.class 显示公共信息
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { public java.lang.String info; public static final int COUNTS; public com.atguigu.java1.JavapTest(); public void showInfo(); } - ② javap -protected JavapTest.class 显示受保护的/公共类和成员
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { protected char gender; public java.lang.String info; public static final int COUNTS; public com.atguigu.java1.JavapTest(); protected char showGender(); public void showInfo(); } - ③ javap -p(或-private)JavapTest.class 显示所有类和成员(大于private的)
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { private int num; boolean flag; protected char gender; public java.lang.String info; public static final int COUNTS; public com.atguigu.java1.JavapTest(); private com.atguigu.java1.JavapTest(boolean); private void methodPrivate(); int getNum(int); protected char showGender(); public void showInfo(); static {}; } - ④ javap -package JavapTest.class 显示程序包/受保护的/公共类和成员(默认),非私有的信息
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { boolean flag; protected char gender; public java.lang.String info; public static final int COUNTS; public com.atguigu.java1.JavapTest(); int getNum(int); protected char showGender(); public void showInfo(); static {}; } - ⑤ javap -sysinfo JavapTest.class 显示正在处理的类的系统信息(路径,大小,日期,MD5 散列,源文件名)
Classfile /Users/dongshuhuan/JavaProjects/JVM_study/src/com/dsh/jvmp2/chapter01/java1/JavapTest.class Last modified 2020-9-21; size 1358 bytes MD5 checksum 526b4a845e4d98180438e4c5781b7e88 Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { boolean flag; protected char gender; public java.lang.String info; public static final int COUNTS; public com.atguigu.java1.JavapTest(); int getNum(int); protected char showGender(); public void showInfo(); static {}; } - ⑥ javap -constants JavapTest.class
- 与前面命令的主要区别,常量值会显示出来
COUNTS = 1;
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { boolean flag; protected char gender; public java.lang.String info; public static final int COUNTS = 1; public com.atguigu.java1.JavapTest(); int getNum(int); protected char showGender(); public void showInfo(); static {}; } - 与前面命令的主要区别,常量值会显示出来
与细节相关
- -s 输出内部类型签名(描述符)
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { boolean flag; descriptor: Z protected char gender; descriptor: C public java.lang.String info; descriptor: Ljava/lang/String; public static final int COUNTS; descriptor: I public com.atguigu.java1.JavapTest(); descriptor: ()V int getNum(int); descriptor: (I)I protected char showGender(); descriptor: ()C public void showInfo(); descriptor: ()V static {}; descriptor: ()V } - -l 输出行号和本地变量表
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { boolean flag; protected char gender; public java.lang.String info; public static final int COUNTS; public com.atguigu.java1.JavapTest(); LineNumberTable: line 20: 0 line 18: 4 line 22: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this Lcom/atguigu/java1/JavapTest; int getNum(int); LineNumberTable: line 30: 0 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lcom/atguigu/java1/JavapTest; 0 7 1 i I protected char showGender(); LineNumberTable: line 33: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/atguigu/java1/JavapTest; public void showInfo(); LineNumberTable: line 36: 0 line 37: 3 line 38: 30 LocalVariableTable: Start Length Slot Name Signature 0 31 0 this Lcom/atguigu/java1/JavapTest; 3 28 1 i I static {}; LineNumberTable: line 15: 0 line 16: 3 LocalVariableTable: Start Length Slot Name Signature } - -c 对代码进行反汇编
Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest { boolean flag; protected char gender; public java.lang.String info; public static final int COUNTS; public com.atguigu.java1.JavapTest(); Code: 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 int getNum(int); Code: 0: aload_0 1: getfield #5 // Field num:I 4: iload_1 5: iadd 6: ireturn protected char showGender(); Code: 0: aload_0 1: getfield #6 // Field gender:C 4: ireturn public void showInfo(); Code: 0: bipush 10 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 static {}; Code: 0: ldc #14 // String www.atguigu.com 2: astore_0 3: return } - -v -verbose 输出附加信息(包括行号、本地变量表、反汇编等详细信息),-v是最全的
Classfile /Users/dongshuhuan/JavaProjects/JVM_study/src/com/dsh/jvmp2/chapter01/java1/JavapTest.class Last modified 2020-9-21; size 1358 bytes MD5 checksum 526b4a845e4d98180438e4c5781b7e88 Compiled from "JavapTest.java" public class com.atguigu.java1.JavapTest minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #16.#46 // java/lang/Object."<init>":()V #2 = String #47 // java #3 = Fieldref #15.#48 // com/atguigu/java1/JavapTest.info:Ljava/lang/String; #4 = Fieldref #15.#49 // com/atguigu/java1/JavapTest.flag:Z #5 = Fieldref #15.#50 // com/atguigu/java1/JavapTest.num:I #6 = Fieldref #15.#51 // com/atguigu/java1/JavapTest.gender:C #7 = Fieldref #52.#53 // java/lang/System.out:Ljava/io/PrintStream; #8 = Class #54 // java/lang/StringBuilder #9 = Methodref #8.#46 // java/lang/StringBuilder."<init>":()V #10 = Methodref #8.#55 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #11 = Methodref #8.#56 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; #12 = Methodref #8.#57 // java/lang/StringBuilder.toString:()Ljava/lang/String; #13 = Methodref #58.#59 // java/io/PrintStream.println:(Ljava/lang/String;)V #14 = String #60 // www.atguigu.com #15 = Class #61 // com/atguigu/java1/JavapTest #16 = Class #62 // 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 Lcom/atguigu/java1/JavapTest; #35 = Utf8 (Z)V #36 = Utf8 methodPrivate #37 = Utf8 getNum #38 = Utf8 (I)I #39 = Utf8 i #40 = Utf8 showGender #41 = Utf8 ()C #42 = Utf8 showInfo #43 = Utf8 <clinit> #44 = Utf8 SourceFile #45 = Utf8 JavapTest.java #46 = NameAndType #28:#29 // "<init>":()V #47 = Utf8 java #48 = NameAndType #23:#24 // info:Ljava/lang/String; #49 = NameAndType #19:#20 // flag:Z #50 = NameAndType #17:#18 // num:I #51 = NameAndType #21:#22 // gender:C #52 = Class #63 // java/lang/System #53 = NameAndType #64:#65 // out:Ljava/io/PrintStream; #54 = Utf8 java/lang/StringBuilder #55 = NameAndType #66:#67 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #56 = NameAndType #66:#68 // append:(I)Ljava/lang/StringBuilder; #57 = NameAndType #69:#70 // toString:()Ljava/lang/String; #58 = Class #71 // java/io/PrintStream #59 = NameAndType #72:#73 // println:(Ljava/lang/String;)V #60 = Utf8 www.atguigu.com #61 = Utf8 com/atguigu/java1/JavapTest #62 = Utf8 java/lang/Object #63 = Utf8 java/lang/System #64 = Utf8 out #65 = Utf8 Ljava/io/PrintStream; #66 = Utf8 append #67 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #68 = Utf8 (I)Ljava/lang/StringBuilder; #69 = Utf8 toString #70 = Utf8 ()Ljava/lang/String; #71 = Utf8 java/io/PrintStream #72 = Utf8 println #73 = Utf8 (Ljava/lang/String;)V { 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 public com.atguigu.java1.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 20: 0 line 18: 4 line 22: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this Lcom/atguigu/java1/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 30: 0 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lcom/atguigu/java1/JavapTest; 0 7 1 i 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 33: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/atguigu/java1/JavapTest; public void showInfo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=2, args_size=1 0: bipush 10 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 LineNumberTable: line 36: 0 line 37: 3 line 38: 30 LocalVariableTable: Start Length Slot Name Signature 0 31 0 this Lcom/atguigu/java1/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 15: 0 line 16: 3 LocalVariableTable: Start Length Slot Name Signature } SourceFile: "JavapTest.java"
组合使用
-v 不包含私有信息,所以可以使用组合方式获取更全的信息
java -v -p JavapTest.class
Classfile /Users/dongshuhuan/JavaProjects/JVM_study/src/com/dsh/jvmp2/chapter01/java1/JavapTest.class //字节码文件所属的路径
Last modified 2020-9-21; size 1358 bytes //最后修改时间,字节码文件大小
MD5 checksum 526b4a845e4d98180438e4c5781b7e88 //MD5散列值
Compiled from "JavapTest.java" //源文件名称
public class com.atguigu.java1.JavapTest //全类名
minor version: 0 //副版本
major version: 52 //主版本
flags: ACC_PUBLIC, ACC_SUPER //访问标识
Constant pool: //常量池
#1 = Methodref #16.#46 // java/lang/Object."<init>":()V
...(中间省略一大坨)
#73 = Utf8 (Ljava/lang/String;)V
###################### 字段表集合的信息start ###########################
{ //字段表集合的信息
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
####################### 字段情况end ###########################
####################### 方法表集合信息start ###########################
public com.atguigu.java1.JavapTest(); //构造器1的信息
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 20: 0
line 18: 4
line 22: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Lcom/atguigu/java1/JavapTest;
private com.atguigu.java1.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: iload_1
12: putfield #4 // Field flag:Z
15: return
LineNumberTable:
line 23: 0
line 18: 4
line 24: 10
line 25: 15
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 this Lcom/atguigu/java1/JavapTest;
0 16 1 flag Z
private void methodPrivate();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 28: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Lcom/atguigu/java1/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 30: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/atguigu/java1/JavapTest;
0 7 1 i 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 33: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/atguigu/java1/JavapTest;
public void showInfo();
descriptor: ()V //方法描述符:方法的形参列表、返回值类型
flags: ACC_PUBLIC //方法的访问标识
Code: //方法的Code属性
stack=3, locals=2, args_size=1 //stack: 操作数栈的最大深度
//locals: 局部变量表的长度
//args_size: 方法接收参数的个数
//偏移量 操作码 操作数
0: bipush 10
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 36: 0
line 37: 3
line 38: 30
//局部变量表:描述内部局部变量的相关信息,这里有2个 this 和 i
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 this Lcom/atguigu/java1/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 15: 0
line 16: 3
LocalVariableTable:
Start Length Slot Name Signature
}
SourceFile: "JavapTest.java" //附加属性: 指明当前字节码文件对应的源程序文件名
小结
- 通过javap命令可以查看一个java类反汇编得到的Class文件版本号、常量池、访问标识、变量表、指令代码行号表等等信息。不显示类索引、父类索引、接口索引集合、()、()等结构
- 通过对前面例子代码反汇编文件的简单分析,可以发现,一个方法的执行通常会涉及下面几块内存的操作:
- (1) java栈中:局部变量表、操作数栈。
- (2) java堆。通过对象的地址引用去操作。
- (3) 常量池。
- (4)其他如帧数据区、方法区的剩余部分等情况,测试中没有显示出来,这里说明一下。
- 平常,我们比较关注的是java类中每个方法的反汇编中的指令操作过程,这些指令都是顺序执行的,可以参考官方文档查看每个指令的含义,很简单: docs.oracle.com/javase/spec…