2019-07-11

Java字節(jié)碼詳解(二)字節(jié)碼的運(yùn)行過(guò)程

2018年10月23日 17:31:04 talex 閱讀數(shù) 677

<article class="baidu_pl" style="box-sizing: inherit; outline: 0px; margin: 0px; padding: 16px 0px 0px; display: block; position: relative; color: rgba(0, 0, 0, 0.75); font-family: -apple-system, "SF UI Text", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: common-ligatures; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

文章目錄

前一章講述了java字節(jié)碼文件的生成以及字節(jié)碼文件中各個(gè)字段代表的含義惩猫,在本章節(jié)將講述字節(jié)碼是什么運(yùn)行的

JVM的一些基礎(chǔ)概念

要理解java字節(jié)碼的運(yùn)行情況羔巢,首先要了解有關(guān)JVM的一些知識(shí)交惯,這些是java字節(jié)碼運(yùn)行的先決條件墅诡。

JVM數(shù)據(jù)類(lèi)型

Java是靜態(tài)類(lèi)型的,它會(huì)影響字節(jié)碼指令的設(shè)計(jì)障癌,這樣指令就會(huì)期望自己對(duì)特定類(lèi)型的值進(jìn)行操作凌外。例如,就會(huì)有好幾個(gè)add指令用于兩個(gè)數(shù)字相加:iadd涛浙、ladd康辑、fadd、dadd轿亮。他們期望類(lèi)型的操作數(shù)分別是int疮薇、long、float和double哀托。大多數(shù)字節(jié)碼都有這樣的特性惦辛,它具有不同形式的相同功能劳秋,這取決于操作數(shù)類(lèi)型仓手。
JVM定義的數(shù)據(jù)類(lèi)型包括:

  1. 基本類(lèi)型:
    • 數(shù)值類(lèi)型: byte (8位), short (16位), int (32位), long (64-bit位), char (16位無(wú)符號(hào)Unicode), float(32-bit IEEE 754 單精度浮點(diǎn)型), double (64-bit IEEE 754 雙精度浮點(diǎn)型)
    • 布爾類(lèi)型
    • 指針類(lèi)型: 指令指針。
  2. 引用類(lèi)型:
    • 類(lèi)
    • 數(shù)組
    • 接口

在字節(jié)碼中布爾類(lèi)型的支持是受限的玻淑。舉例來(lái)說(shuō)嗽冒,沒(méi)有結(jié)構(gòu)能直接操作布爾值。布爾值被替換轉(zhuǎn)換成 int 是通過(guò)編譯器來(lái)進(jìn)行的补履,并且最終還是被轉(zhuǎn)換成 int 結(jié)構(gòu)添坊。Java 開(kāi)發(fā)者應(yīng)該熟悉所有上面的類(lèi)型,除了 returnAddress箫锤,它沒(méi)有等價(jià)的編程語(yǔ)言類(lèi)型贬蛙。類(lèi)數(shù)組接口在字節(jié)碼中布爾類(lèi)型的支持是受限的。舉例來(lái)說(shuō)谚攒,沒(méi)有結(jié)構(gòu)能直接操作布爾值阳准。布爾值被替換轉(zhuǎn)換成 int 是通過(guò)編譯器來(lái)進(jìn)行的,并且最終還是被轉(zhuǎn)換成 int 結(jié)構(gòu)馏臭。

Java 開(kāi)發(fā)者應(yīng)該熟悉所有上面的類(lèi)型野蝇,除了 returnAddress,它沒(méi)有等價(jià)的編程語(yǔ)言類(lèi)型。

JVM的內(nèi)存結(jié)構(gòu)

Java字節(jié)碼詳解(二)字節(jié)碼的運(yùn)行過(guò)程

JVM的內(nèi)存分布如上圖所示绕沈。方法區(qū)和堆是線程共享的锐想,而寄存器、java方法棧乍狐、本地方法棧是各個(gè)線程私有的赠摇。

1.方法區(qū)

方法區(qū)是用來(lái)存儲(chǔ)已被JVM加載的類(lèi)信息、常量浅蚪、靜態(tài)變量蝉稳、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
這個(gè)區(qū)域很少進(jìn)行垃圾回收掘鄙,回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類(lèi)型的卸載耘戚。

2.堆

此區(qū)域唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存.

3.PC寄存器

程序計(jì)數(shù)器是一塊較小的內(nèi)存空間操漠,線程私有收津。它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器

4. Java方法棧和本地方法棧

JVM棧描述的是java方法執(zhí)行的內(nèi)存模型,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀浊伙,用于存儲(chǔ)局部變量表撞秋、操作數(shù)棧、動(dòng)態(tài)鏈接嚣鄙、方法出口等信息.
Java字節(jié)碼的運(yùn)行就是在JVM方法棧中進(jìn)行的

Java字節(jié)碼運(yùn)行過(guò)程

簡(jiǎn)單的示例

1.示例源碼

先來(lái)看我們的例子代碼吻贿,源碼如下:

public class Test{
    public static void main(String[] args){
        Integer a = 1;
        Integer b = 2;
        Integer c = a + b;
    }
}

2.main函數(shù)的字節(jié)碼展示

使用javac Test.java進(jìn)行編譯,然后使用javap -v Test.class查看該java文件的字節(jié)碼哑子,為了排除干擾舅列,去除了很多不必要的字節(jié)碼

  *** 省略部分字節(jié)碼
 Constant pool:
   #1 = Methodref           #5.#14         // java/lang/Object."<init>":()V
   #2 = Methodref           #15.#16        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #3 = Methodref           #15.#17        // java/lang/Integer.intValue:()I                                                                                                                                                                                                                                                                                             
  *** 省略部分字節(jié)碼

  public static void main(java.lang.String[]);                                                               
   descriptor: ([Ljava/lang/String;)V                                                                        
   flags: ACC_PUBLIC, ACC_STATIC                                                                             
   Code:                                                                                                     
     stack=2, locals=4, args_size=1                                                                          
        0: iconst_1                                                                                          
        1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;      
        4: astore_1                                                                                          
        5: iconst_2                                                                                          
        6: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;      
        9: astore_2                                                                                          
       10: aload_1                                                                                           
       11: invokevirtual #3                  // Method java/lang/Integer.intValue:()I                        
       14: aload_2                                                                                           
       15: invokevirtual #3                  // Method java/lang/Integer.intValue:()I                        
       18: iadd                                                                                              
       19: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;      
       22: astore_3                                                                                          
       23: return                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             

3.字節(jié)碼指令運(yùn)行過(guò)程

接下來(lái)分析Code中字節(jié)碼運(yùn)行的過(guò)程。這里說(shuō)一下卧蜓,每個(gè)指令前的數(shù)字為指令在寄存器中的偏移量帐要。

0: iconst_1 將int常量1進(jìn)行放入操作數(shù)棧。這里稍微做個(gè)拓展弥奸,如果將float常量2進(jìn)行入棧操作榨惠,name該指令是fconst_2,詳細(xì)的指令種類(lèi)及意義請(qǐng)查看下一章 Java字節(jié)碼指令詳解盛霎。

在這里插入圖片描述

1: invokestatic #2 調(diào)用常量池中序號(hào)為#2的靜態(tài)方法赠橙,這里調(diào)用的是 Integer.valueOf()方法,表示將該int類(lèi)型進(jìn)行裝箱操作愤炸,變?yōu)镮nteger類(lèi)型

4: astore_1 在索引為1的位置將第一個(gè)操作數(shù)出棧(一個(gè)Integer值)并且將其存進(jìn)本地變量期揪,相當(dāng)于變量a。

在這里插入圖片描述

5: iconst_2 將int常量2進(jìn)行放入操作數(shù)棧

在這里插入圖片描述

6: invokestatic #2 調(diào)用常量池中序號(hào)為#2的靜態(tài)方法摇幻,這里調(diào)用的是 Integer.valueOf()方法横侦,表示將該int類(lèi)型進(jìn)行裝箱操作挥萌,變?yōu)镮nteger類(lèi)型

9: astore_2 在索引為2的位置將第一個(gè)操作數(shù)出棧(一個(gè)Integer值)并且將其存進(jìn)本地變量,相當(dāng)于變量b枉侧。

在這里插入圖片描述

10: aload_1 從索引1的本地變量中加載一個(gè)int值引瀑,放入操作數(shù)棧

在這里插入圖片描述

11: invokevirtual #3 調(diào)用常量池中序號(hào)為#3的實(shí)例方法,這里調(diào)用的是 Integer.intValue()方法

14: aload_2 從索引1的本地變量中加載一個(gè)int值榨馁,放入操作數(shù)棧

在這里插入圖片描述

15: invokevirtual #3 調(diào)用常量池中序號(hào)為#3的實(shí)例方法憨栽,這里調(diào)用的是 Integer.intValue()方法

18: iadd 把操作數(shù)棧中的前兩個(gè)int值出棧并相加,將相加的結(jié)果放入操作數(shù)棧翼虫。

在這里插入圖片描述

19: invokestatic #2調(diào)用常量池中序號(hào)為#2的靜態(tài)方法屑柔,這里調(diào)用的是 Integer.valueOf()方法

22: astore_3 在索引為3的位置將第一個(gè)操作數(shù)出棧(一個(gè)Integer值)并且將其存進(jìn)本地變量,相當(dāng)于變量c珍剑。

在這里插入圖片描述

23: return 方法結(jié)束

方法調(diào)用

上面的示例是比較簡(jiǎn)單的掸宛,而且只有一個(gè)main函數(shù),接下來(lái)將展示在多個(gè)函數(shù)時(shí)候字節(jié)碼的形式以及運(yùn)行的具體過(guò)程招拙。這里就直接拿參考文章的示例唧瘾,原文寫(xiě)得真的很好,有條件可以去看英文原文。 字節(jié)碼的介紹

1.示例源碼

public class Test{
    public static void main(String[] args){
        int a = 1;
        int b = 2;
        int c = calc(1,2);
    }

    static int calc(int a,int b){
        return (int) Math.sqrt(Math.pow(a,2)+Math.pow(b,2));
    }
}

2.字節(jié)碼展示

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iconst_1
         5: iconst_2
         6: invokestatic  #2                  // Method calc:(II)I
         9: istore_3
        10: return

  static int calc(int, int);
    descriptor: (II)I
    flags: ACC_STATIC
    Code:
      stack=6, locals=2, args_size=2
         0: iload_0
         1: i2d
         2: ldc2_w        #3                  // double 2.0d
         5: invokestatic  #5                  // Method java/lang/Math.pow:(DD)D
         8: iload_1
         9: i2d
        10: ldc2_w        #3                  // double 2.0d
        13: invokestatic  #5                  // Method java/lang/Math.pow:(DD)D
        16: dadd
        17: invokestatic  #6                  // Method java/lang/Math.sqrt:(D)D
        20: d2i
        21: ireturn

3. 指令執(zhí)行過(guò)程詳解

上面就是main方法和calc方法的字節(jié)碼别凤,由于main方法的指令跟上個(gè)例子很相似饰序,唯一不同的是 c=a+b變?yōu)橛蒫alc方法去執(zhí)行并且返回。這里就不再贅述main方法规哪,接下來(lái)主要講解calc方法的執(zhí)行過(guò)程求豫。

0: iload_0 將方法中第一個(gè)參數(shù)入棧

在這里插入圖片描述

1: i2d將int類(lèi)型轉(zhuǎn)為double類(lèi)型

2: ldc2_w #3 將常量池序號(hào)為#3的long型常量從常量池推送至棧頂(寬索引)

在這里插入圖片描述

5: invokestatic #5 調(diào)用靜態(tài)方法:Math.pow:() ,并且將結(jié)果放入棧頂

在這里插入圖片描述

8: iload_1
9: i2d
10: ldc2_w #3
13: invokestatic #5
以上的指令跟上一個(gè)一樣诉稍,進(jìn)行平方運(yùn)算

在這里插入圖片描述

16: dadd 將result和result2相加蝠嘉,并推入棧頂

在這里插入圖片描述

17: invokestatic #6 調(diào)用Math.sqrt()方法

20: d2i 將double類(lèi)型轉(zhuǎn)為int類(lèi)型

21: ireturn 返回int類(lèi)型的數(shù)值

實(shí)例調(diào)用

修改上面的代碼,加入對(duì)象均唉,并調(diào)用對(duì)象的方法是晨。

public class Test {
    public static void main(String[] args){

       Point a  =new Point (1,2);
       Point b = new Point (3,4);
       int c = a.area(b);
    }

    static class Point{
        private int x;
        private int y;

        public Point(int x,int y){
            this.x = x;
            this.y = y;
        }

        public int  area(Point p){
            int length  = Math.abs(p.y-this.y);
            int width  = Math.abs(p.x-this.x);
            return length*width;
        }
    }
}

使用javap -v Test查看編譯后的字節(jié)碼:

  public static void main(java.lang.String[]);                                                    
    descriptor: ([Ljava/lang/String;)V                                                            
    flags: ACC_PUBLIC, ACC_STATIC                                                                 
    Code:                                                                                         
      stack=4, locals=4, args_size=1                                                              
         0: new           #2                  // class Test3$Point                                
         3: dup                                                                                   
         4: iconst_1                                                                              
         5: iconst_2                                                                              
         6: invokespecial #3                  // Method Test3$Point."<init>":(II)V                
         9: astore_1                                                                              
        10: new           #2                  // class Test3$Point                                
        13: dup                                                                                   
        14: iconst_3                                                                              
        15: iconst_4                                                                              
        16: invokespecial #3                  // Method Test3$Point."<init>":(II)V                
        19: astore_2                                                                              
        20: aload_1                                                                               
        21: aload_2                                                                               
        22: invokevirtual #4                  // Method Test3$Point.area:(LTest3$Point;)I         
        25: istore_3                                                                              
        26: return                                                                                

這個(gè)main方法比上一個(gè)例子多了幾個(gè)新的指令:new,dup,invokespecial

  • new new 指令與編程語(yǔ)言中的 new 運(yùn)算符類(lèi)似,它根據(jù)傳入的操作數(shù)所指定類(lèi)型來(lái)創(chuàng)建對(duì)象(這是對(duì) Point 類(lèi)的符號(hào)引用)舔箭。

  • dup dup指令會(huì)復(fù)制頂部操作數(shù)的棧值,這意味著現(xiàn)在我們?cè)跅m敳坑袃蓚€(gè)指向Point對(duì)象的引用蚊逢。

    在這里插入圖片描述

  • iconst_1层扶,iconst_2invokespecial烙荷,將x,y的值(1,2)壓入棧頂镜会,接下來(lái)進(jìn)行Point初始化工作,將x,y的值進(jìn)行賦值终抽。初始化完成后會(huì)將棧頂?shù)娜齻€(gè)操作引用銷(xiāo)毀戳表,只留下最初的Point的對(duì)象引用桶至。

    在這里插入圖片描述

  • astore_1 將該P(yáng)oint引用出棧,并將其賦值到索引1所保存的本地變量(astore_1中的a表明這是一個(gè)引用值)

    在這里插入圖片描述

接下來(lái)進(jìn)行第二個(gè)Point實(shí)例的初始化和賦值操作


在這里插入圖片描述
  • 20: aload_1,21: aload_2 將a,b的Point實(shí)例的引用入棧
  • 22: invokevirtual #4 調(diào)用 area方法匾旭,
  • 25: istore_3 將返回值放入索引3中(即賦值給c)
  • return 方法結(jié)束

總結(jié)

本章節(jié)寫(xiě)了字節(jié)碼運(yùn)行的詳細(xì)過(guò)程镣屹,詳細(xì)的指令介紹在下一章,有興趣可以看看价涝。

</article>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末女蜈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子色瘩,更是在濱河造成了極大的恐慌伪窖,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件居兆,死亡現(xiàn)場(chǎng)離奇詭異覆山,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)泥栖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)汹买,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人聊倔,你說(shuō)我怎么就攤上這事晦毙。” “怎么了耙蔑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵见妒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我甸陌,道長(zhǎng)须揣,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任钱豁,我火速辦了婚禮耻卡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牲尺。我一直安慰自己卵酪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布谤碳。 她就那樣靜靜地躺著溃卡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜒简。 梳的紋絲不亂的頭發(fā)上瘸羡,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音搓茬,去河邊找鬼犹赖。 笑死队他,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的峻村。 我是一名探鬼主播麸折,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雀哨!你這毒婦竟也來(lái)了磕谅?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤雾棺,失蹤者是張志新(化名)和其女友劉穎膊夹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捌浩,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡放刨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尸饺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片进统。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖浪听,靈堂內(nèi)的尸體忽然破棺而出螟碎,到底是詐尸還是另有隱情,我是刑警寧澤迹栓,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布掉分,位于F島的核電站,受9級(jí)特大地震影響克伊,放射性物質(zhì)發(fā)生泄漏酥郭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一愿吹、第九天 我趴在偏房一處隱蔽的房頂上張望不从。 院中可真熱鬧,春花似錦犁跪、人聲如沸椿息。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)撵颊。三九已至,卻和暖如春惫叛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逞刷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工嘉涌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妻熊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓仑最,卻偏偏與公主長(zhǎng)得像扔役,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子警医,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Java byte code 的學(xué)習(xí)意義 為啥要學(xué)java bytecode亿胸,這就跟你問(wèn)我已經(jīng)會(huì)python了為...
    shanggl閱讀 1,662評(píng)論 0 3
  • 概述 執(zhí)行引擎是Java虛擬機(jī)最核心的組成部分之一≡せ剩“虛擬機(jī)”是一個(gè)相對(duì)于“物理機(jī)”的概念侈玄,這兩個(gè)機(jī)器都有代碼執(zhí)行...
    胡二囧閱讀 888評(píng)論 2 2
  • 這篇文章是我之前翻閱了不少的書(shū)籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方吟温,同時(shí)不同JDK版本的...
    高廣超閱讀 15,595評(píng)論 3 83
  • 每個(gè)使用Java的開(kāi)發(fā)者都知道Java字節(jié)碼是在JRE中運(yùn)行(JRE: Java 運(yùn)行時(shí)環(huán)境)序仙。JVM則是JRE中...
    燕京博士閱讀 1,406評(píng)論 0 6
  • 第二部分 自動(dòng)內(nèi)存管理機(jī)制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運(yùn)行數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,159評(píng)論 0 2