原创

JVM虚拟机详解------javap概述及实战示例

1. javap相关

javap是官方提供的,用于反编译一个或多个.class文件的java工具,使用javap命令,我们可以查看类的具体信息,有助于我们分析代码的运行状态,更好的理解jvm工作原理。

javap官方文档

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岁的程序员,致力于让学习变得更有趣!
现在关注作者即可领取海量学习资料与简历模板
在这里插入图片描述

往期好文:

用Python每天给女神发一句手机短信情话

MySQL优化之explain

Spring源码分析-MVC初始化

春风得意马蹄疾,一文看尽(JVM)虚拟机

造轮子的艺术

源码阅读技巧

Java注解详解

教你自建SpringBoot服务器

更多文章请点击

正文到此结束