天天看点

javap命令解析字节码

1.javap命令是什么?

可以通过javap命令看到java程序在执行过程中,每一句代码真正地做了什么,包括cpu的指令和jvm具体做了什么,可以在发生一些错误或者奇怪的事情的时候,知道为什么会这样。

javap是jdk自带的反解析工具。作用是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

这些信息当中,有些信息,比如本地变量表、指令和代码偏移映射表,常量池中的方法的参数名称等,需要在使用javac编译成class文件时,指定参数才能输出。使用javac -g xx.java就可以生成所有相关的信息了。

通过局部变量表,我们可以查看局部变量的作用域范围,所在槽位等信息,甚至可以看到槽位复用等信息。

javap的用法格式

javap

其中classes就是你要反编译的class文件。不需要带后缀.class。

命令行中直接输入javap或者javap -help可以看到javap的options:

用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?                输出此用法消息
  -version                         版本信息
  -v  -verbose                     输出附加信息
  -l                               输出行号和本地变量表
  -public                          仅显示公共类和成员
  -protected                       显示受保护的/公共类和成员
  -package                         显示程序包/受保护的/公共类
                                   和成员 (默认)
  -p  -private                     显示所有类和成员
  -c                               对代码进行反汇编
  -s                               输出内部类型签名
  -sysinfo                         显示正在处理的类的
                                   系统信息 (路径, 大小, 日期, MD5 散列)
  -constants                       显示最终常量
  --module <模块>, -m <模块>       指定包含要反汇编的类的模块
  --module-path <路径>             指定查找应用程序模块的位置
  --system <jdk>                   指定查找系统模块的位置
  --class-path <路径>              指定查找用户类文件的位置
  -classpath <路径>                指定查找用户类文件的位置
  -cp <路径>                       指定查找用户类文件的位置
  -bootclasspath <路径>            覆盖引导类文件的位置
           

一般常用的是-v -l -c三个选项

javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。

javap -l 会输出行号和本地变量表信息

javap -c 会对当前class字节码进行反编译生成汇编代码。

也可以通过jclasslib工具来看到上面的信息

2.一些测试

主要介绍code区(汇编指令)、局部变量表和代码偏移映射三个部分

创建一个TestDate.java文件

import java.util.Date;
public class TestDate {
    
    private int count = 0;
    
    public static void main(String[] args) {
        TestDate testDate = new TestDate();
        testDate.test1();
    }
    
    public void test1(){
        Date date = new Date();
        String name1 = "wangerbei";
        test2(date,name1); 
        System.out.println(date+name1);
    }

    public void test2(Date dateP,String name2){
        dateP = null;
        name2 = "zhangsan";
    }

    public void test3(){
        count++;
    }
    
    public void  test4(){
        int a = 0;
        {
            int b = 0;
            b = a+1;
        }
        int c = a+1;
    }
}
           

使用命令:javac - g TestDate.java,生成一个TestDate.class文件

再使用javap命令对字节码文件进行反汇编,使用命令javap -c -l TestDate

此时不需要加后缀.class

Compiled from "TestDate.java"
public class TestDate {
 //默认的构造方法,在构造方法执行时完成一些初始化操作,包括一些成员变量的初始化赋值等
  public TestDate();
    Code:
       0: aload_0    //从本地变量表中加载索引为0的变量的值,也就是this的引用,压入栈
       1: invokespecial #1                  // 调用方法 java/lang/Object."<init>":()V 完成this对象的初始化
       4: aload_0  //
       5: iconst_0
       6: putfield      #2                  // Field count:I
       9: return
    LineNumberTable:
      line 2: 0
      line 4: 4
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      10     0  this   LTestDate;

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class TestDate
       3: dup
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #5                  // Method test1:()V
      12: return
    LineNumberTable:
      line 7: 0
      line 8: 8
      line 9: 12
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      13     0  args   [Ljava/lang/String;
          8       5     1 testDate   LTestDate;

  public void test1();
    Code:
       0: new           #6                  // class java/util/Date
       3: dup
       4: invokespecial #7                  // Method java/util/Date."<init>":()V
       7: astore_1
       8: ldc           #8                  // String wangerbei
      10: astore_2
      11: aload_0
      12: aload_1
      13: aload_2
      14: invokevirtual #9                  // Method test2:(Ljava/util/Date;Ljava/lang/String;)V
      17: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
      20: aload_1
      21: aload_2
      22: invokedynamic #11,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/util/Date;Ljava/lang/String;)Ljava/lang/String;
      27: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      30: return
    LineNumberTable:
      line 12: 0
      line 13: 8
      line 14: 11
      line 15: 17
      line 16: 30
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      31     0  this   LTestDate;
          8      23     1  date   Ljava/util/Date;
         11      20     2 name1   Ljava/lang/String;

  public void test2(java.util.Date, java.lang.String);
    Code:
       0: aconst_null
       1: astore_1
       2: ldc           #13                 // String zhangsan
       4: astore_2
       5: return
    LineNumberTable:
      line 19: 0
      line 20: 2
      line 21: 5
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       6     0  this   LTestDate;
          0       6     1 dateP   Ljava/util/Date;
          0       6     2 name2   Ljava/lang/String;

  public void test3();
    Code:
       0: aload_0
       1: dup
       2: getfield      #2                  // Field count:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field count:I
      10: return
    LineNumberTable:
      line 24: 0
      line 25: 10
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      11     0  this   LTestDate;

  public void test4();
    Code:
       0: iconst_0
       1: istore_1
       2: iconst_0
       3: istore_2
       4: iload_1
       5: iconst_1
       6: iadd
       7: istore_2
       8: iload_1
       9: iconst_1
      10: iadd
      11: istore_2
      12: return
    LineNumberTable:
      line 28: 0
      line 30: 2
      line 31: 4
      line 33: 8
      line 34: 12
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          4       4     2     b   I
          0      13     0  this   LTestDate;
          2      11     1     a   I
         12       1     2     c   I
}
           

参考资料:https://www.jianshu.com/p/6a8997560b05