国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項超值服

開通VIP
通過javap命令分析java匯編指令

一、javap命令簡述

javap是jdk自帶的反解析工具。它的作用就是根據(jù)class字節(jié)碼文件,反解析出當(dāng)前類對應(yīng)的code區(qū)(匯編指令)、本地變量表、異常表和代碼行偏移量映射表、常量池等等信息。
當(dāng)然這些信息中,有些信息(如本地變量表、指令和代碼行偏移量映射表、常量池中方法的參數(shù)名稱等等)需要在使用javac編譯成class文件時,指定參數(shù)才能輸出,比如,你直接javac xx.java,就不會在生成對應(yīng)的局部變量表等信息,如果你使用javac -g xx.java就可以生成所有相關(guān)信息了。如果你使用的eclipse,則默認(rèn)情況下,eclipse在編譯時會幫你生成局部變量表、指令和代碼行偏移量映射表等信息的。
通過反編譯生成的匯編代碼,我們可以深入的了解java代碼的工作機(jī)制。比如我們可以查看i ;這行代碼實際運(yùn)行時是先獲取變量i的值,然后將這個值加1,最后再將加1后的值賦值給變量i。
通過局部變量表,我們可以查看局部變量的作用域范圍、所在槽位等信息,甚至可以看到槽位復(fù)用等信息。

javap的用法格式:
javap <options> <classes>
其中classes就是你要反編譯的class文件。
在命令行中直接輸入javap或javap -help可以看到j(luò)avap的options有如下選項:

-help --help -? 輸出此用法消息 -version 版本信息,其實是當(dāng)前javap所在jdk的版本信息,不是class在哪個jdk下生成的。 -v -verbose 輸出附加信息(包括行號、本地變量表,反匯編等詳細(xì)信息) -l 輸出行號和本地變量表 -public 僅顯示公共類和成員 -protected 顯示受保護(hù)的/公共類和成員 -package 顯示程序包/受保護(hù)的/公共類 和成員 (默認(rèn)) -p -private 顯示所有類和成員 -c 對代碼進(jìn)行反匯編 -s 輸出內(nèi)部類型簽名 -sysinfo 顯示正在處理的類的系統(tǒng)信息 (路徑, 大小, 日期, MD5 散列) -constants 顯示靜態(tài)最終常量 -classpath <path> 指定查找用戶類文件的位置 -bootclasspath <path> 覆蓋引導(dǎo)類文件的位置

一般常用的是-v -l -c三個選項。
javap -v classxx,不僅會輸出行號、本地變量表信息、反編譯匯編代碼,還會輸出當(dāng)前類用到的常量池等信息。
javap -l 會輸出行號和本地變量表信息。
javap -c 會對當(dāng)前class字節(jié)碼進(jìn)行反編譯生成匯編代碼。
查看匯編代碼時,需要知道里面的jvm指令,可以參考官方文檔:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html
另外通過jclasslib工具也可以看到上面這些信息,而且是可視化的,效果更好一些。

二、javap測試及內(nèi)容詳解

前面已經(jīng)介紹過javap輸出的內(nèi)容有哪些,東西比較多,這里主要介紹其中code區(qū)(匯編指令)、局部變量表和代碼行偏移映射三個部分。
如果需要分析更多的信息,可以使用javap -v進(jìn)行查看。
另外,為了更方便理解,所有匯編指令不單拎出來講解,而是在反匯編代碼中以注釋的方式講解(吐槽一下,簡書的markdown貌似不能改字體顏色,這一點(diǎn)很不爽)。

下面寫段代碼測試一下:
例子1:分析一下下面的代碼反匯編之后結(jié)果:

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 生成class文件,然后通過javap命令對字節(jié)碼進(jìn)行反匯編:
$ javap -c -l TestDate
得到下面內(nèi)容(指令等部分是我參照著官方文檔總結(jié)的):

Warning: Binary file TestDate contains com.justest.test.TestDateCompiled from 'TestDate.java'public class com.justest.test.TestDate { //默認(rèn)的構(gòu)造方法,在構(gòu)造方法執(zhí)行時主要完成一些初始化操作,包括一些成員變量的初始化賦值等操作 public com.justest.test.TestDate(); Code: 0: aload_0 //從本地變量表中加載索引為0的變量的值,也即this的引用,壓入棧 1: invokespecial #10 //出棧,調(diào)用java/lang/Object.'<init>':()V 初始化對象,就是this指定的對象的init()方法完成初始化 4: aload_0 // 4到6表示,調(diào)用this.count = 0,也即為count復(fù)制為0。這里this引用入棧 5: iconst_0 //將常量0,壓入到操作數(shù)棧 6: putfield //出棧前面壓入的兩個值(this引用,常量值0), 將0取出,并賦值給count 9: return//指令與代碼行數(shù)的偏移對應(yīng)關(guān)系,每一行第一個數(shù)字對應(yīng)代碼行數(shù),第二個數(shù)字對應(yīng)前面code中指令前面的數(shù)字 LineNumberTable: line 5: 0 line 7: 4 line 5: 9 //局部變量表,start length表示這個變量在字節(jié)碼中的生命周期起始和結(jié)束的偏移位置(this生命周期從頭0到結(jié)尾10),slot就是這個變量在局部變量表中的槽位(槽位可復(fù)用),name就是變量名稱,Signatur局部變量類型描述 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/justest/test/TestDate; public static void main(java.lang.String[]); Code:// new指令,創(chuàng)建一個class com/justest/test/TestDate對象,new指令并不能完全創(chuàng)建一個對象,對象只有在初,只有在調(diào)用初始化方法完成后(也就是調(diào)用了invokespecial指令之后),對象才創(chuàng)建成功, 0: new //創(chuàng)建對象,并將對象引用壓入棧 3: dup //將操作數(shù)棧定的數(shù)據(jù)復(fù)制一份,并壓入棧,此時棧中有兩個引用值 4: invokespecial #20 //pop出棧引用值,調(diào)用其構(gòu)造函數(shù),完成對象的初始化 7: astore_1 //pop出棧引用值,將其(引用)賦值給局部變量表中的變量testDate 8: aload_1 //將testDate的引用值壓入棧,因為testDate.test1();調(diào)用了testDate,這里使用aload_1從局部變量表中獲得對應(yīng)的變量testDate的值并壓入操作數(shù)棧 9: invokevirtual #21 // Method test1:()V 引用出棧,調(diào)用testDate的test1()方法 12: return //整個main方法結(jié)束返回 LineNumberTable: line 10: 0 line 11: 8 line 12: 12 //局部變量表,testDate只有在創(chuàng)建完成并賦值后,才開始聲明周期 LocalVariableTable: Start Length Slot Name Signature 0 13 0 args [Ljava/lang/String; 8 5 1 testDate Lcom/justest/test/TestDate; public void test1(); Code: 0: new #27 // 0到7創(chuàng)建Date對象,并賦值給date變量 3: dup 4: invokespecial #29 // Method java/util/Date.'<init>':()V 7: astore_1 8: ldc #30 // String wangerbei,將常量“wangerbei”壓入棧 10: astore_2 //將棧中的“wangerbei”pop出,賦值給name1 11: aload_0 //11到14,對應(yīng)test2(date,name1);默認(rèn)前面加this. 12: aload_1 //從局部變量表中取出date變量 13: aload_2 //取出name1變量 14: invokevirtual #32 // Method test2: (Ljava/util/Date;Ljava/lang/String;)V 調(diào)用test2方法 // 17到38對應(yīng)System.out.println(date name1); 17: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream; //20到35是jvm中的優(yōu)化手段,多個字符串變量相加,不會兩兩創(chuàng)建一個字符串對象,而使用StringBuilder來創(chuàng)建一個對象 20: new #42 // class java/lang/StringBuilder 23: dup 24: invokespecial #44 // Method java/lang/StringBuilder.'<init>':()V 27: aload_1 28: invokevirtual #45 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 31: aload_2 32: invokevirtual #49 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: invokevirtual #52 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 38: invokevirtual #56 // Method java/io/PrintStream.println:(Ljava/lang/String;)V invokevirtual指令表示基于類調(diào)用方法 41: return LineNumberTable: line 15: 0 line 16: 8 line 17: 11 line 18: 17 line 19: 41 LocalVariableTable: Start Length Slot Name Signature 0 42 0 this Lcom/justest/test/TestDate; 8 34 1 date Ljava/util/Date; 11 31 2 name1 Ljava/lang/String; public void test2(java.util.Date, java.lang.String); Code: 0: aconst_null //將一個null值壓入棧 1: astore_1 //將null賦值給dateP 2: ldc #66 // String zhangsan 從常量池中取出字符串“zhangsan”壓入棧中 4: astore_2 //將字符串賦值給name2 5: return LineNumberTable: line 22: 0 line 23: 2 line 24: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/justest/test/TestDate; 0 6 1 dateP Ljava/util/Date; 0 6 2 name2 Ljava/lang/String; public void test3(); Code: 0: aload_0 //取出this,壓入棧 1: dup //復(fù)制操作數(shù)棧棧頂?shù)闹担喝霔?,此時有兩個this對象引用值在操作數(shù)組棧 2: getfield #12// Field count:I this出棧,并獲取其count字段,然后壓入棧,此時棧中有一個this和一個count的值 5: iconst_1 //取出一個int常量1,壓入操作數(shù)棧 6: iadd // 從棧中取出count和1,將count值和1相加,結(jié)果入棧 7: putfield #12 // Field count:I 一次彈出兩個,第一個彈出的是上一步計算值,第二個彈出的this,將值賦值給this的count字段 10: return LineNumberTable: line 27: 0 line 28: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this Lcom/justest/test/TestDate; 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 33: 0 line 35: 2 line 36: 4 line 38: 8 line 39: 12 //看下面,b和c的槽位slot一樣,這是因為b的作用域就在方法塊中,方法塊結(jié)束,局部變量表中的槽位就被釋放,后面的變量就可以復(fù)用這個槽位 LocalVariableTable: Start Length Slot Name Signature 0 13 0 this Lcom/justest/test/TestDate; 2 11 1 a I 4 4 2 b I 12 1 2 c I}

例子2:下面一個例子
先有一個User類:

public class User {    private String name;    private int age;     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }     public int getAge() {        return age;    }     public void setAge(int age) {        this.age = age;    }}

然后寫一個操作User對象的測試類:

public class TestUser { private int count; public void test(int a){ count = count a; } public User initUser(int age,String name){ User user = new User(); user.setAge(age); user.setName(name); return user; } public void changeUser(User user,String newName){ user.setName(newName); }}

先javac -g 編譯成class文件。
然后對TestUser類進(jìn)行反匯編:
$ javap -c -l TestUser
得到反匯編結(jié)果如下:

Warning: Binary file TestUser contains com.justest.test.TestUserCompiled from 'TestUser.java'public class com.justest.test.TestUser {//默認(rèn)的構(gòu)造函數(shù)  public com.justest.test.TestUser();    Code:       0: aload_0       1: invokespecial #10                 // Method java/lang/Object.'<init>':()V       4: return    LineNumberTable:      line 3: 0    LocalVariableTable:      Start  Length  Slot  Name   Signature             0       5     0  this   Lcom/justest/test/TestUser;  public void test(int);    Code:       0: aload_0 //取this對應(yīng)的對應(yīng)引用值,壓入操作數(shù)棧       1: dup //復(fù)制棧頂?shù)臄?shù)據(jù),壓入棧,此時棧中有兩個值,都是this對象引用       2: getfield      #18 // 引用出棧,通過引用獲得對應(yīng)count的值,并壓入棧       5: iload_1 //從局部變量表中取得a的值,壓入棧中       6: iadd //彈出棧中的count值和a的值,進(jìn)行加操作,并將結(jié)果壓入棧       7: putfield      #18 // 經(jīng)過上一步操作后,棧中有兩個值,棧頂為上一步操作結(jié)果,棧頂下面是this引用,這一步putfield指令,用于將棧頂?shù)闹蒂x值給引用對象的count字段      10: return //return void    LineNumberTable:      line 8: 0      line 9: 10    LocalVariableTable:      Start  Length  Slot  Name   Signature             0      11     0  this   Lcom/justest/test/TestUser;             0      11     1     a   I  public com.justest.test.User initUser(int, java.lang.String);    Code:       0: new           #23   // class com/justest/test/User 創(chuàng)建User對象,并將引用壓入棧       3: dup //復(fù)制棧頂值,再次壓入棧,棧中有兩個User對象的地址引用       4: invokespecial #25   // Method com/justest/test/User.'<init>':()V 調(diào)用user對象初始化       7: astore_3 //從棧中pop出User對象的引用值,并賦值給局部變量表中user變量       8: aload_3 //從局部變量表中獲得user的值,也就是User對象的地址引用,壓入棧中       9: iload_1 //從局部變量表中獲得a的值,并壓入棧中,注意aload和iload的區(qū)別,一個取值是對象引用,一個是取int類型數(shù)據(jù)      10: invokevirtual #26  // Method com/justest/test/User.setAge:(I)V 操作數(shù)棧pop出兩個值,一個是User對象引用,一個是a的值,調(diào)用setAge方法,并將a的值傳給這個方法,setAge操作的就是堆中對象的字段了      13: aload_3 //同7,壓入棧      14: aload_2 //從局部變量表取出name,壓入棧      15: invokevirtual #29  // MethodUser.setName:(Ljava/lang/String;)V 操作數(shù)棧pop出兩個值,一個是User對象引用,一個是name的值,調(diào)用setName方法,并將a的值傳給這個方法,setName操作的就是堆中對象的字段了      18: aload_3 //從局部變量取出User引用,壓入棧      19: areturn //areturn指令用于返回一個對象的引用,也就是上一步中User的引用,這個返回值將會被壓入調(diào)用當(dāng)前方法的那個方法的棧中objectref is popped from the operand stack of the current frame ([§2.6](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.6)) and pushed onto the operand stack of the frame of the invoker    LineNumberTable:      line 12: 0      line 13: 8      line 14: 13      line 15: 18    LocalVariableTable:      Start  Length  Slot  Name   Signature             0      20     0  this   Lcom/justest/test/TestUser;             0      20     1   age   I             0      20     2  name   Ljava/lang/String;             8      12     3  user   Lcom/justest/test/User;  public void changeUser(com.justest.test.User, java.lang.String);    Code:       0: aload_1 //局部變量表中取出this,也即TestUser對象引用,壓入棧       1: aload_2 //局部變量表中取出newName,壓入棧       2: invokevirtual #29 // Method User.setName:(Ljava/lang/String;)V pop出棧newName值和TestUser引用,調(diào)用其setName方法,并將newName的值傳給這個方法       5: return    LineNumberTable:      line 19: 0      line 20: 5    LocalVariableTable:      Start  Length  Slot  Name   Signature             0       6     0  this   Lcom/justest/test/TestUser;             0       6     1  user   Lcom/justest/test/User;             0       6     2 newName   Ljava/lang/String;public static void main(java.lang.String[]);    Code:       0: new      #1 // class com/justest/test/TestUser 創(chuàng)建TestUser對象,將引用壓入棧       3: dup //復(fù)制引用,壓入棧       4: invokespecial #43   // Method '<init>':()V 引用值出棧,調(diào)用構(gòu)造方法,對象初始化       7: astore_1 //引用值出棧,賦值給局部變量表中變量tu       8: aload_1 //取出tu值,壓入棧       9: bipush    10 //將int值10壓入棧      11: ldc           #44   // String wangerbei 從常量池中取出“wangerbei” 壓入棧      13: invokevirtual #46    // Method initUser(ILjava/lang/String;)Lcom/justest/test/User; 調(diào)用tu的initUser方法,并返回User對象 ,出棧三個值:tu引用,10和“wangerbei”,并且initUser方法的返回值,即User的引用,也會被壓入棧中,參考前面initUser中的areturn指令      16: astore_2 //User引用出棧,賦值給user變量      17: aload_1 //取出tu值,壓入棧      18: aload_2 //取出user值,壓入棧      19: ldc           #48     // String lisi 從常量池中取出“l(fā)isi”壓入棧      21: invokevirtual #50     // Method changeUser:(Lcom/justest/test/User;Ljava/lang/String;)V 調(diào)用tu的changeUser方法,并將user引用和lisi傳給這個方法      24: return //return void    LineNumberTable:      line 23: 0      line 24: 8      line 25: 17      line 26: 24    LocalVariableTable:      Start  Length  Slot  Name   Signature             0      25     0  args   [Ljava/lang/String;             8      17     1    tu   Lcom/justest/test/TestUser;            17       8     2  user   Lcom/justest/test/User;}

三、總結(jié)

1、通過javap命令可以查看一個java類反匯編、常量池、變量表、指令代碼行號表等等信息。

2、平常,我們比較關(guān)注的是java類中每個方法的反匯編中的指令操作過程,這些指令都是順序執(zhí)行的,可以參考官方文檔查看每個指令的含義,很簡單:


3、通過對前面兩個例子代碼反匯編中各個指令操作的分析,可以發(fā)現(xiàn),一個方法的執(zhí)行通常會涉及下面幾塊內(nèi)存的操作:

(1)java棧中:局部變量表、操作數(shù)棧。這些操作基本上都值操作。
(2)java堆。通過對象的地址引用去操作。
(3)常量池。
(4)其他如幀數(shù)據(jù)區(qū)、方法區(qū)(jdk1.8之前,常量池也在方法區(qū))等部分,測試中沒有顯示出來,這里說明一下。

在做值相關(guān)操作時:
一個指令,可以從局部變量表、常量池、堆中對象、方法調(diào)用、系統(tǒng)調(diào)用中等取得數(shù)據(jù),這些數(shù)據(jù)(可能是指,可能是對象的引用)被壓入操作數(shù)棧。
一個指令,也可以從操作數(shù)數(shù)棧中取出一到多個值(pop多次),完成賦值、加減乘除、方法傳參、系統(tǒng)調(diào)用等等操作。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java棧和局部變量操作(二)
關(guān)于Java虛擬機(jī)中的字節(jié)碼指令
大話+圖說:Java匯編指令
javap
JVM真香系列:.java文件到.class文件
深入理解JVM內(nèi)幕:從基本結(jié)構(gòu)到Java 7新特性
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服