查看原文
其他

走进字节码之查看工具

GeekJunz 极客创享会 2019-07-14

引入

之前已经讲述了如何在 windows 环境通过 命令行的方式编译Java 代码。

cmd命令行 编译Java 文件

此篇文章作为JVM 系列的引入,简要的说明如何在 windows 平台进行 Java 编译后的 class 文件的查看。其实 JDK 中已经内置了这些工具。可以在 %JAVA_HOME%\bin 目录下看到 javap.exe 这个可执行文件。一些其他的工具在该目录下也都能看到。本文只做基本演示,后面详细介绍字节码时会逐行详细分析。

示例代码

此处就举一个最简单不过的 Java 示例程序,代码如下:

1public class Test{
2
3    private int m;
4
5    public int inc(){
6        return m + 1;
7    }
8
9}

利器 javap

进入命令行,编译完源码后,会看到源码文件旁有个同名的 .class 文件。接着执行如下命令

1javap -c Test  (或 javap -c Test.class)

可以看到如下结果:

1F:\tmp\jvmTmp>javap -c Test
2
3Compiled from "Test.java"
4public class Test {
5  public Test();
6    Code:
7       0: aload_0
8       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
9       4return
10
11  public int inc();
12    Code:
13       0: aload_0
14       1: getfield      #2                  // Field m:I
15       4: iconst_1
16       5: iadd
17       6: ireturn
18}

这只是展示了当前类和方法的信息。如果要查看详细内容包括常量池、行号表、异常表等信息,可以通过如下命令查看:

1javap -verbose Test

执行命令结果如下:

1F:\tmp\jvmTmp>javap -verbose Test
2
3Classfile /F:/tmp/jvmTmp/Test.class
4  Last modified 2018-7-18; size 265 bytes
5  MD5 checksum 0d5efc4b65ae7eb6d64f84136ce58ff9
6  Compiled from "Test.java"
7public class Test
8  minor version: 0
9  major version: 52
10  flags: ACC_PUBLIC, ACC_SUPER
11Constant pool:
12   #1 = Methodref          #4.#15         // java/lang/Object."<init>":()V
13   #2 = Fieldref           #3.#16         // Test.m:I
14   #3 = Class              #17            // Test
15   #4 = Class              #18            // java/lang/Object
16   #5 = Utf8               m
17   #6 = Utf8               I
18   #7 = Utf8               <init>
19   #8 = Utf8               ()V
20   #9 = Utf8               Code
21  #10 = Utf8               LineNumberTable
22  #11 = Utf8               inc
23  #12 = Utf8               ()I
24  #13 = Utf8               SourceFile
25  #14 = Utf8               Test.java
26  #15 = NameAndType        #7:#8          // "<init>":()V
27  #16 = NameAndType        #5:#6          // m:I
28  #17 = Utf8               Test
29  #18 = Utf8               java/lang/Object
30{
31  public Test();
32    descriptor: ()V
33    flags: ACC_PUBLIC
34    Code:
35      stack=1, locals=1, args_size=1
36         0: aload_0
37         1: invokespecial #1             // Method java/lang/Object."<init>":()V
38         4return
39      LineNumberTable:
40        line 10
41
42  public int inc();
43    descriptor: ()I
44    flags: ACC_PUBLIC
45    Code:
46      stack=2, locals=1, args_size=1
47         0: aload_0
48         1: getfield      #2             // Field m:I
49         4: iconst_1
50         5: iadd
51         6: ireturn
52      LineNumberTable:
53        line 60
54}
55SourceFile: "Test.java"

可以看到多了文件路径、文件校验、上次修改时间、大小、版本信息、常量池等信息。这是展示字节码信息最丰富的命令了。class 文件中的信息几乎都展示出来了。其中第 11 行可以看到 Constant pool 就是常量池的意思,即行号 12 到 29 都是常量池部分。常量池中的常量通常以 #开头接上序号。

1#3 = Class              #17            // Test

如上所示就是第三个常量,= 号后面是该常量项的类型,后面 #17,表示指向第 17 个常量,这里不做展开。

更多用法

当然,javap 的用法不知这些,可以通过如下命令查看所有用法:

1F:\tmp\jvmTmp>javap -help
2
3用法: javap <options> <classes>
4其中, 可能的选项包括:
5  -help  --help  -?        输出此用法消息
6  -version                 版本信息
7  -v  -verbose             输出附加信息
8  -l                       输出行号和本地变量表
9  -public                  仅显示公共类和成员
10  -protected               显示受保护的/公共类和成员
11  -package                 显示程序包/受保护的/公共类
12                           和成员 (默认)
13  -p  -private             显示所有类和成员
14  -c                       对代码进行反汇编
15  -s                       输出内部类型签名
16  -sysinfo                 显示正在处理的类的
17                           系统信息 (路径, 大小, 日期, MD5 散列)
18  -constants               显示最终常量
19  -classpath <path>        指定查找用户类文件的位置
20  -cp <path>               指定查找用户类文件的位置
21  -bootclasspath <path>    覆盖引导类文件的位置

大家可以都尝试下,加深印象。


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存