05.局部變量表與操作數棧

1) 概述

  1. JVM的字節碼執行引擎,功能基本就是輸入字節碼文件,然后對字節碼進行解析并處理,最后輸出執行的結果。
  2. 實現方式可能有通過解釋器直接執行字節碼,或者適通過及時編譯器產生本地代碼,也就是編譯執行,當讓也可能兩者皆有。
    • HotSpot就是兩者皆有,使用頻率較多的代碼JIT動態編譯為本地代碼,頻率較少的就解釋執行。

2) 棧幀概述

  1. 棧幀是用于執行JVM進行方法調用和方法執行的數據結構。
  2. 棧幀隨著方法調用而創建,隨著方法結束而銷毀。
  3. 棧幀里面存儲了方法的 局部變量操作數棧、動態連接、方法返回地址等信息。

棧幀的概念結構.png

3) 局部變量表

  1. 局部變量表:用來存放方法參數和方法內部定義的局部變量的存儲空間。
  2. 以slot為單位,目前一個slot存放32位以內的數據類型
  3. 對于64位的數據占2個slot
  4. 對于實例方法,第0位slot存放的是this,然后從1到n,依次分配給參數列表
    • 對于靜態方法,則從0位開始依次分配給參數列表
  5. 然后根據方法體內部定義的變量順序和作用域來分配slot
public class Test1 {
    public int add(int a, int b) {
        int c = a + b;
        return a + b + c;
        /* javap -verbose 得到slot分配情況
         LocalVariableTable:
           Start  Length  Slot  Name   Signature
               0      10     0  this   Lcom/jvm/stack/Test1;
               0      10     1     a   I
               0      10     2     b   I
               4       6     3     c   I
         */
    }
}
  1. slot是復用的,以節省棧幀的空間,這種設計可能會影響到系統的垃圾收集行為。
// -Xms10m -Xmx10m
public static void main(String[] args) {
  {
      byte[] bs1 = new byte[1024 * 1204];
      byte[] bs2 = new byte[1024 * 1204];
      byte[] bs3 = new byte[1024 * 1204];

      /* 此時slot的情況
      0 -- args -- 堆引用
      1 -- bs1  -- 堆引用
      2 -- bs2  -- 堆引用
      3 -- bs3  -- 堆引用
      */

      System.gc();
      printMemory(); // freeMemory :2.98663330078125

      bs1 = null;
      /* 顯式將bs1置為null, slot 1則空閑出來*/
  }
  System.gc();
  printMemory(); // freeMemory :4.8661346435546875

  /* 代碼塊結束,上述的bs1 bs2 bs3變量的作用域已經結束,
  所占用的slot可以被后續定義的變量復用 */

  int a = 5;
  int b = 5;

  /* 此時slot的使用情況
      0 -- args -- 堆引用
      1 -- a    -- I
      2 -- b    -- I
      3 -- bs3  -- 堆引用
   */

  System.gc();
  printMemory(); // freeMemory :6.8662872314453125

  String c = "a";
  /* 此時slot的使用情況
     0 -- args -- 堆引用
     1 -- a    -- I
     2 -- b    -- I
     3 -- c  -- 堆引用
   */

  System.gc();
  printMemory(); // freeMemory :8.866722106933594
}

public static void printMemory() {
  //System.out.println("totalMemory:" + Runtime.getRuntime().totalMemory()/1024.0/1024.0);
  System.out.println("freeMemory :" + Runtime.getRuntime().freeMemory()/1024.0/1024.0);
  //System.out.println("maxMemory  :" + Runtime.getRuntime().maxMemory()/1024.0/1024.0);
}

4) 操作數棧

  1. 操作數棧:用來存放方法運行期間,各個指令操作的數據。
  2. 操作數棧中元素的數據類型必須和字節碼指令的順序嚴格匹配
    • 數據類型和插槽位置的數據類型必須一一對應:比如指令 iconst_1,那么插槽1位置的類型必須是I
  3. 虛擬機在實現棧幀的時候可能會做一些優化,讓兩個棧幀出現部分重疊區域,以存放公用的數據
package com.lc.sprnigcloud.stack;

/**
 * @author hahadasheng
 * @since 2020/12/28
 */
public class Test {

    public static void main(String[] args) {
        test();
    }

    /* LocalVariableTable:
    Start  Length  Slot  Name   Signature
        2      51     0     a   I
       16      37     1     b   I
       35      18     2     c   I
     */
    public static void test() {
        int a = 1;
        a = a++;
        /*
         0: iconst_1            -> 常數1
         1: istore_0            -> 將常數1存放在局部變量表slot_0的位置,也就是a
         2: iload_0             -> 將slot_0中存放a的值1 放入棧中
         3: iinc          0, 1  -> slot_0中a的值自增1,1 + 1 = 2
         6: istore_0            -> 將棧中的值1賦值到slot_0的位置,所以此時a的值還是為1
         */
        System.out.println(a); // 1

        int b = 1;
        b = b++ * ++b;
        /*
        14: iconst_1            -> 常數1
        15: istore_1            -> 將1放在slot_1的位置
        16: iload_1             -> 將slot_1的值1入棧 棧:[1]
        17: iinc          1, 1  -> slot_1位置的1自增1,1+1=2
        20: iinc          1, 1  -> slot_1位置的2自增1,2+1=3
        23: iload_1             -> 將slot_1的值3入棧 棧:[3,1]
        24: imul                -> 將棧中的兩個數相乘 3 * 1 = 3,棧中的值為3
        25: istore_1            -> 將棧中值3放在slot_1的位置
         */
        System.out.println(b); // 3

        int c = 1;
        c = ++c * c++;
        /*
        33: iconst_1            -> 常數1
        34: istore_2            -> 將常數1放在局部變量表slot_2的位置
        35: iinc          2, 1  -> slot_2位置數自增1,1+1=2
        38: iload_2             -> 將slot_2位置的2入棧 棧:[2]
        39: iload_2             -> 將slot_2位置的2入棧 棧:[2,2]
        40: iinc          2, 1  -> 將slot_2位置的2自增1,2+1=3
        43: imul                -> 將棧中的值取出來進行乘法操作 2*2=4,棧中的值變為4
        44: istore_2            -> 將棧中值4存放在slot_2的位置
         */
        System.out.println(c); // 4
    }
}

5) 動態連接

  1. 動態連接:每個棧幀持有一個指向運行時常量池中該棧幀所屬方法的引用,以支持方法調用過程中的動態連接。
  2. 動態連接分類:
    1. 靜態解析:類加載的時候,符號引用就轉化成直接引用
    2. 動態連接:運行期間轉換為直接引用(動態分派)

6) 方法返回地址

  1. 方法返回地址:方法執行后返回的地址,
    • 無論正常退出還是異常退出,都得返回到方法被調用的位置,程序才能繼續執行

7) 方法調用

  1. 方法調用:方法調用就是確定具體調用哪一個方法,并不涉及方法內部的執行過程。
    • 不一定要調用這個方法
  2. 部分方法是直接在類加載的解析階段,就確定了直接引用關系
    • 靜態方法、私有方法、實例構造器、父類方法
  3. 但是對于實例方法,也稱虛方法,因為重載和多態,需要運行期間動態委派
    • 例如虛擬機調用此方法的指令為 invokevirtual

8) 靜態分派和動態分派

  1. 分派:分為靜態分派和動態分派

  2. 靜態分派:所有依賴靜態類型來定位方法執行版本的分派方式

    • 比如:重載方法(依據傳參確定)
  3. 動態分派:根據運行期間的實際類型來定位方法執行版本的分派方式,

    • 比如:重寫覆蓋方法、實現的接口等
  4. 單分派和多分派:就是按照分派思考的緯度,多于一個的就算多分派,只有一個的稱為單分派

    • 只有一個確認的,沒有重載、重寫、多態等可能有多個可能的就是單分派
  5. 如何執行方法中的字節碼指令:JVM通過基于棧的字節碼解釋器引擎來執行指令,JVM的指令集也是基于棧的。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容