JVM虚拟机详解------javap概述及实战示例
1. javap相关
javap是官方提供的,用于反编译一个或多个.class文件的java工具,使用javap命令,我们可以查看类的具体信息,有助于我们分析代码的运行状态,更好的理解jvm工作原理。
2. javap使用格式
javap <options> <classes>
3. option集
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
4. javap命令展示
4.1. -version查看版本信息
javap -version
1.8.0_211
4.2. -v -verbose查看附加信息
javap -v -verbose Main.class
Classfile /D:/project/idea/doit/out/production/doit/cn/yzstu/jvm/Main.class
Last modified 2021-1-30; size 673 bytes
MD5 checksum 63f7245b740da5d47dae0b4216057254
Compiled from "Main.java"
public class cn.yzstu.jvm.Main
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#28 // java/lang/Object."<init>":()V
#2 = Class #29 // java/lang/Thread
#3 = Methodref #2.#28 // java/lang/Thread."<init>":()V
#4 = Class #30 // cn/yzstu/jvm/Math
#5 = Methodref #4.#28 // cn/yzstu/jvm/Math."<init>":()V
#6 = Methodref #4.#31 // cn/yzstu/jvm/Math.compute:()I
#7 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Methodref #34.#35 // java/io/PrintStream.println:(I)V
#9 = Class #36 // cn/yzstu/jvm/Main
#10 = Class #37 // java/lang/Object
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lcn/yzstu/jvm/Main;
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
#20 = Utf8 args
#21 = Utf8 [Ljava/lang/String;
#22 = Utf8 math
#23 = Utf8 Lcn/yzstu/jvm/Math;
#24 = Utf8 compute
#25 = Utf8 I
#26 = Utf8 SourceFile
#27 = Utf8 Main.java
#28 = NameAndType #11:#12 // "<init>":()V
#29 = Utf8 java/lang/Thread
#30 = Utf8 cn/yzstu/jvm/Math
#31 = NameAndType #24:#38 // compute:()I
#32 = Class #39 // java/lang/System
#33 = NameAndType #40:#41 // out:Ljava/io/PrintStream;
#34 = Class #42 // java/io/PrintStream
#35 = NameAndType #43:#44 // println:(I)V
#36 = Utf8 cn/yzstu/jvm/Main
#37 = Utf8 java/lang/Object
#38 = Utf8 ()I
#39 = Utf8 java/lang/System
#40 = Utf8 out
#41 = Utf8 Ljava/io/PrintStream;
#42 = Utf8 java/io/PrintStream
#43 = Utf8 println
#44 = Utf8 (I)V
{
public cn.yzstu.jvm.Main();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/yzstu/jvm/Main;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: invokespecial #3 // Method java/lang/Thread."<init>":()V
7: pop
8: new #4 // class cn/yzstu/jvm/Math
11: dup
12: invokespecial #5 // Method cn/yzstu/jvm/Math."<init>":()V
15: astore_1
16: aload_1
17: invokevirtual #6 // Method cn/yzstu/jvm/Math.compute:()I
20: istore_2
21: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_2
25: invokevirtual #8 // Method java/io/PrintStream.println:(I)V
28: return
LineNumberTable:
line 8: 0
line 9: 8
line 10: 16
line 11: 21
line 12: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
16 13 1 math Lcn/yzstu/jvm/Math;
21 8 2 compute I
}
SourceFile: "Main.java"
这一部分信息,建议大家配合我的另一篇文章jvm栈工作原理,里面有分析这些信息的工具
4.3. -l
javap -l Main.class
Compiled from "Main.java"
public class cn.yzstu.jvm.Main {
public cn.yzstu.jvm.Main();
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/yzstu/jvm/Main;
public static void main(java.lang.String[]);
LineNumberTable:
line 8: 0
line 9: 8
line 10: 16
line 11: 21
line 12: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
16 13 1 math Lcn/yzstu/jvm/Math;
21 8 2 compute I
}
4.4. 其他
剩下的懒得去展示了,自己去执行命令看吧(狗头)
5. javap实战示例
在jvm栈工作原理中其实有些许体现,但是有些地方没讲清楚,这里说一下我在使用javap命令时的思路
5.1. 反编译
首先我们拿到的是.class文件,里面都是字节码,难以阅读,所以首先需要反编译.class字节码,获取指令码
javap -c Main.class > Main.txt
上面的命令可以将我们的Main.class字节码文件反汇编成指令码,并且保存到同路径下的Main.txt文件中
Compiled from "Main.java"
public class cn.yzstu.jvm.Main {
public cn.yzstu.jvm.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/Thread
3: dup
4: invokespecial #3 // Method java/lang/Thread."<init>":()V
7: pop
8: new #4 // class cn/yzstu/jvm/Math
11: dup
12: invokespecial #5 // Method cn/yzstu/jvm/Math."<init>":()V
15: astore_1
16: aload_1
17: invokevirtual #6 // Method cn/yzstu/jvm/Math.compute:()I
20: istore_2
21: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_2
25: invokevirtual #8 // Method java/io/PrintStream.println:(I)V
28: return
}
看不懂指令码的朋友可以扫描文章最下方我的公众号二维码,关注后回复“jvm指令手册”获取文档
5.2. 查看附加信息
有些指令码后面带有类似“#5”之类的参数,这些参数代表的意义可以在附加信息中查看,执行一下命令
javap -v Main.class
在执行结果中的常量池中可以看到其对应的信息
Constant pool:
#1 = Methodref #10.#28 // java/lang/Object."<init>":()V
#2 = Class #29 // java/lang/Thread
#3 = Methodref #2.#28 // java/lang/Thread."<init>":()V
#4 = Class #30 // cn/yzstu/jvm/Math
#5 = Methodref #4.#28 // cn/yzstu/jvm/Math."<init>":()V
#6 = Methodref #4.#31 // cn/yzstu/jvm/Math.compute:()I
#7 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Methodref #34.#35 // java/io/PrintStream.println:(I)V
#9 = Class #36 // cn/yzstu/jvm/Main
#10 = Class #37 // java/lang/Object
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lcn/yzstu/jvm/Main;
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
#20 = Utf8 args
#21 = Utf8 [Ljava/lang/String;
#22 = Utf8 math
#23 = Utf8 Lcn/yzstu/jvm/Math;
#24 = Utf8 compute
#25 = Utf8 I
#26 = Utf8 SourceFile
#27 = Utf8 Main.java
#28 = NameAndType #11:#12 // "<init>":()V
#29 = Utf8 java/lang/Thread
#30 = Utf8 cn/yzstu/jvm/Math
#31 = NameAndType #24:#38 // compute:()I
#32 = Class #39 // java/lang/System
#33 = NameAndType #40:#41 // out:Ljava/io/PrintStream;
#34 = Class #42 // java/io/PrintStream
#35 = NameAndType #43:#44 // println:(I)V
#36 = Utf8 cn/yzstu/jvm/Main
#37 = Utf8 java/lang/Object
#38 = Utf8 ()I
#39 = Utf8 java/lang/System
#40 = Utf8 out
#41 = Utf8 Ljava/io/PrintStream;
#42 = Utf8 java/io/PrintStream
#43 = Utf8 println
#44 = Utf8 (I)V
这就是阅读指令码的简单实战
6. 总结
javap是一个强大工具类,熟练使用后能够帮助你更好的理解java运行机制。
我是Baldwin,一个25岁的程序员,致力于让学习变得更有趣!
现在关注作者即可领取海量学习资料与简历模板
往期好文:
正文到此结束