JVM - 結合代碼示例徹底搞懂Java內存區域_線程棧 | 本地方法棧 | 程序計數器


在這裏插入圖片描述

Pre

JVM-01Java內存區域與內存溢出異常(上)【運行時區域數據】

JVM-02內存區域與內存溢出異常(中)【hotspot虛擬機對象】

JVM-03內存區域與內存溢出異常(下)【OutOfMemoryError案例】


運行時數據區總覽

在這裏插入圖片描述

字節碼文件被裝載子系統裝載到JVM中,字節碼執行引擎負責執行這些字節碼文件。
裝載子系統和執行引擎都是C++的實現。

裝載子系統: JVM-白話聊一聊JVM類加載和雙親委派機制源碼解析

我們重點關注下運行時數據區域 ,先關注線程私有的這3個部分。


線程棧

概要

沒給方法被執行的時候,JVM都會同步創建一個棧幀。

這個棧和數據結構的棧結構是一樣的, FILO .

舉個例子 ,方法A 中調用了方法B , 代碼先執行方法A ,此時方法A 入棧, 然後調用方法B,這個時候方法B入棧 。 當方法B執行結束後,方法B出棧,回到方法A執行的地方,方法A繼續執行,執行結束 ,方法A出棧。

在這裏插入圖片描述


棧內部主要組成部分

【Java代碼】

   public int doSomething() {
        int a = 1 ;
        int b = 2 ;
        int c = (a + b) * 10 ;
        return c;
    }

【javap -c 反彙編】

如何操作的,見文末 ,JVM字節碼指令集也見文末

  public int doSomething();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        10
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn
}

0: iconst_1
1: istore_1 【i —> int類型】

iconst_1 是個什麼鬼? 查查操作手冊
在這裏插入圖片描述
istore_1

在這裏插入圖片描述

iconst_0 和 istore_0 是默認存放調用該方法的對象。

這裏就涉及到兩個組成部分 【局部變量】 + 【操作數棧】

局部變量

0x04  iconst_1  將 int1 推送至棧頂
0x3c  istore_1  將棧頂 int 型數值存入第二個本地變量
0x05  iconst_2  將 int2 推送至棧頂
0x3d  istore_2  將棧頂 int 型數值存入第三個本地變量
 

比對代碼

  int a = 1 ;
  int b = 2 ;

iconst_1 , 將 int 1 壓入操作數棧 , istore_1 將棧頂 int 型數值存入第二個本地變量 ,這個時候 會先將 1 出棧,然後存入局部變量表。

istore_1 、istore_2 存入本地變量,就是放到了局部變量表。


操作數棧

0x04  iconst_1  將 int1 推送至棧頂
0x05  iconst_2  將 int2 推送至棧頂

這兩步的意思 就是將代碼中的 a 和 b 對應的值 1 和 2 壓入 操作數棧 。

這個操作數棧 本身也是棧結構, FILO . 入棧 出棧


繼續

 int c = (a + b) * 10 ;
 return c;
 4: iload_1    將第二個 int 型本地變量推送至棧頂  ----> a的值 1 入棧 (操作數棧)
 5: iload_2    將第三個 int 型本地變量推送至棧頂  ----> b的值 2 入棧 (操作數棧)
 6: iadd       將棧頂兩 int 型數值相加並將結果壓入棧頂  ---->  計算 a+b =3,結果壓入棧頂 (a ,b 出棧,計算結果,然後將a+b的結果壓入操作數棧)
 7: bipush        10      將單字節的常量值(-128~127)推送至棧頂  -----> 10 入棧 操作數棧)
 9: imul       將棧頂兩 int 型數值相乘並將結果壓入棧頂  ----> 計算乘積 3 * 10 ,將30壓入操作數棧
 10: istore_3  將棧頂 int 型數值存入第四個本地變量  ------>  給 c賦值 
 11: iload_3   將第四個 int 型本地變量推送至棧頂  ----> 壓入操作數棧 
 12: ireturn   從當前方法返回 int  ----> 返回

上面的行號 7 到9 ? 沒有9 。 我們這個常量10 也要佔位置嘛 。

計算 結果肯定是CPU執行的嘛 ,只不過數據是存放在內存中的。

簡言之,操作數棧,是程序運行期間需要臨時存放數據的內存空間。


動態鏈接

符號引用 替換爲 直接引用。

我們知道在類裝載的過程中,有個過程是【解析】

在這裏插入圖片描述
JVM-白話聊一聊JVM類加載和雙親委派機制源碼解析

舉個例子

 public static void main(String[] args) {
        Artisan artisan = new Artisan();
        artisan.doSomething();
    }

簡單說當應用執行到artisan.doSomething()方法( 非靜態方法),需要找到這個方法在方法區的常量池中的具體的位置,這個過程就是動態鏈接

方法區#運行時常量池 ,是方法區的一部分。 Class文件中的常量池表用於存放編譯期間生成的各種字面量和符號引用,這部分內容將在類加載後放到方法區的運行時常量池中。


方法出口

   public static void main(String[] args) {
        Artisan artisan = new Artisan();
        artisan.doSomething();
    }

    public int doSomething() {
        int a = 1 ;
        int b = 2 ;
        int c = (a + b) * 10 ;
        return c;
    }

doSomething方法執行完要回到main方法中,方法出口中記錄的就是記錄main方法中的位置,不記錄的話 ,不知道回到main方法中的哪一行繼續執行哇~


小結

在這裏插入圖片描述


程序計數器

簡單理解,可以理解爲 記錄程序執行的位置。

線程私有。

Java多線程,當線程A沒有搶到CPU的執行權,如果沒記錄程序執行的位置,等下次搶到CPU執行權的時候,這尼瑪咋弄? 重新開始執行嗎?

顯然是不行的,所以需要程序計數器來給每個線程的執行到的行號做下標記。各個現場的程序計數器互不影響,獨立存儲。

我們來看看javap -c 處理的反彙編

在這裏插入圖片描述

簡單理解,可以理解爲上面的行號, 實際上存儲的是這行代碼對應在內存中的指針位置。

字節碼 由誰來執行? 肯定是字節碼執行引擎嘛 ,所以 字節碼執行引擎肯定知道程序的執行位置,這樣 字節碼執行引擎和程序計數器就關聯起來了。


本地方法棧

native 方法 底層C++的


測試demo

package com.gof.test;

 
public class Artisan {


    public static void main(String[] args) {
        Artisan artisan = new Artisan();
        artisan.doSomething();
    }

    public int doSomething() { // public 類型
        int a = 1 ;
        int b = 2 ;
        int c = (a + b) * 10 ;
        return c;
    }
}

javap

用法: javap <options> <classes>
其中, 可能的選項包括:
  -help  --help  -?        輸出此用法消息
  -version                 版本信息
  -v  -verbose             輸出附加信息
  -l                       輸出行號和本地變量表
  -public                  僅顯示公共類和成員
  -protected               顯示受保護的/公共類和成員
  -package                 顯示程序包/受保護的/公共類
                           和成員 (默認)
  -p  -private             顯示所有類和成員
  -c                       對代碼進行反彙編
  -s                       輸出內部類型簽名
  -sysinfo                 顯示正在處理的類的
                           系統信息 (路徑, 大小, 日期, MD5 散列)
  -constants               顯示最終常量
  -classpath <path>        指定查找用戶類文件的位置
  -cp <path>               指定查找用戶類文件的位置
  -bootclasspath <path>    覆蓋引導類文件的位置

-c 對代碼進行反彙編

 E:\Program Files\Java\jdk1.8.0_161\bin> ./javap -c D:\IdeaProjects\GOF23\target\classes\com\gof\test\Artisan.class > Artisan.txt

查看 Artisan.txt

Compiled from "Artisan.java"
public class com.gof.test.Artisan {
  public com.gof.test.Artisan();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/gof/test/Artisan
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method doSomething:()I
      12: pop
      13: return

  public int doSomething();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        10
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn
}

JVM字節碼指令集手冊

下載地址戳這裏–>提取碼:9ru5

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章