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 將 int 型 1 推送至棧頂
0x3c istore_1 將棧頂 int 型數值存入第二個本地變量
0x05 iconst_2 將 int 型 2 推送至棧頂
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 將 int 型 1 推送至棧頂
0x05 iconst_2 將 int 型 2 推送至棧頂
這兩步的意思 就是將代碼中的 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執行的嘛 ,只不過數據是存放在內存中的。
簡言之,操作數棧,是程序運行期間需要臨時存放數據的內存空間。
動態鏈接
符號引用 替換爲 直接引用。
我們知道在類裝載的過程中,有個過程是【解析】
舉個例子
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
}