天天看點

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