JVM - 结合代码示例彻底搞懂Java内存区域_线程栈 | 本地方法栈 | 程序计数器
文章目錄
- Pre
- 運行時數(shù)據(jù)區(qū)總覽
- 線程棧
- 概要
- 棧內部主要組成部分
- 局部變量
- 操作數(shù)棧
- 動態(tài)鏈接
- 方法出口
- 小結
- 程序計數(shù)器
- 本地方法棧
- 附
- 測試demo
- javap
- JVM字節(jié)碼指令集手冊
Pre
JVM-01Java內存區(qū)域與內存溢出異常(上)【運行時區(qū)域數(shù)據(jù)】
JVM-02內存區(qū)域與內存溢出異常(中)【hotspot虛擬機對象】
JVM-03內存區(qū)域與內存溢出異常(下)【OutOfMemoryError案例】
運行時數(shù)據(jù)區(qū)總覽
字節(jié)碼文件被裝載子系統(tǒng)裝載到JVM中,字節(jié)碼執(zhí)行引擎負責執(zhí)行這些字節(jié)碼文件。
裝載子系統(tǒng)和執(zhí)行引擎都是C++的實現(xiàn)。
裝載子系統(tǒng): JVM-白話聊一聊JVM類加載和雙親委派機制源碼解析
我們重點關注下運行時數(shù)據(jù)區(qū)域 ,先關注線程私有的這3個部分。
線程棧
概要
沒給方法被執(zhí)行的時候,JVM都會同步創(chuàng)建一個棧幀。
這個棧和數(shù)據(jù)結構的棧結構是一樣的, FILO .
舉個例子 ,方法A 中調用了方法B , 代碼先執(zhí)行方法A ,此時方法A 入棧, 然后調用方法B,這個時候方法B入棧 。 當方法B執(zhí)行結束后,方法B出棧,回到方法A執(zhí)行的地方,方法A繼續(xù)執(zhí)行,執(zhí)行結束 ,方法A出棧。
棧內部主要組成部分
【Java代碼】
public int doSomething() {int a = 1 ;int b = 2 ;int c = (a + b) * 10 ;return c;}【javap -c 反匯編】
如何操作的,見文末 ,JVM字節(jié)碼指令集也見文末
public int doSomething();Code:0: iconst_11: istore_12: iconst_23: istore_24: iload_15: iload_26: iadd7: bipush 109: imul10: istore_311: iload_312: ireturn }0: iconst_1
1: istore_1 【i —> int類型】
iconst_1 是個什么鬼? 查查操作手冊
istore_1
iconst_0 和 istore_0 是默認存放調用該方法的對象。
這里就涉及到兩個組成部分 【局部變量】 + 【操作數(shù)棧】
局部變量
0x04 iconst_1 將 int 型 1 推送至棧頂 0x3c istore_1 將棧頂 int 型數(shù)值存入第二個本地變量 0x05 iconst_2 將 int 型 2 推送至棧頂 0x3d istore_2 將棧頂 int 型數(shù)值存入第三個本地變量比對代碼
int a = 1 ;int b = 2 ;iconst_1 , 將 int 1 壓入操作數(shù)棧 , istore_1 將棧頂 int 型數(shù)值存入第二個本地變量 ,這個時候 會先將 1 出棧,然后存入局部變量表。
istore_1 、istore_2 存入本地變量,就是放到了局部變量表。
操作數(shù)棧
0x04 iconst_1 將 int 型 1 推送至棧頂 0x05 iconst_2 將 int 型 2 推送至棧頂這兩步的意思 就是將代碼中的 a 和 b 對應的值 1 和 2 壓入 操作數(shù)棧 。
這個操作數(shù)棧 本身也是棧結構, FILO . 入棧 出棧
繼續(xù)
int c = (a + b) * 10 ;return c; 4: iload_1 將第二個 int 型本地變量推送至棧頂 ----> a的值 1 入棧 (操作數(shù)棧)5: iload_2 將第三個 int 型本地變量推送至棧頂 ----> b的值 2 入棧 (操作數(shù)棧)6: iadd 將棧頂兩 int 型數(shù)值相加并將結果壓入棧頂 ----> 計算 a+b =3,結果壓入棧頂 (a ,b 出棧,計算結果,然后將a+b的結果壓入操作數(shù)棧)7: bipush 10 將單字節(jié)的常量值(-128~127)推送至棧頂 -----> 10 入棧 操作數(shù)棧)9: imul 將棧頂兩 int 型數(shù)值相乘并將結果壓入棧頂 ----> 計算乘積 3 * 10 ,將30壓入操作數(shù)棧10: istore_3 將棧頂 int 型數(shù)值存入第四個本地變量 ------> 給 c賦值 11: iload_3 將第四個 int 型本地變量推送至棧頂 ----> 壓入操作數(shù)棧 12: ireturn 從當前方法返回 int ----> 返回上面的行號 7 到9 ? 沒有9 。 我們這個常量10 也要占位置嘛 。
計算 結果肯定是CPU執(zhí)行的嘛 ,只不過數(shù)據(jù)是存放在內存中的。
簡言之,操作數(shù)棧,是程序運行期間需要臨時存放數(shù)據(jù)的內存空間。
動態(tài)鏈接
符號引用 替換為 直接引用。
我們知道在類裝載的過程中,有個過程是【解析】
JVM-白話聊一聊JVM類加載和雙親委派機制源碼解析
舉個例子
public static void main(String[] args) {Artisan artisan = new Artisan();artisan.doSomething();}簡單說當應用執(zhí)行到artisan.doSomething()方法( 非靜態(tài)方法),需要找到這個方法在方法區(qū)的常量池中的具體的位置,這個過程就是動態(tài)鏈接
方法區(qū)#運行時常量池 ,是方法區(qū)的一部分。 Class文件中的常量池表用于存放編譯期間生成的各種字面量和符號引用,這部分內容將在類加載后放到方法區(qū)的運行時常量池中。
方法出口
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方法執(zhí)行完要回到main方法中,方法出口中記錄的就是記錄main方法中的位置,不記錄的話 ,不知道回到main方法中的哪一行繼續(xù)執(zhí)行哇~
小結
程序計數(shù)器
簡單理解,可以理解為 記錄程序執(zhí)行的位置。
線程私有。
Java多線程,當線程A沒有搶到CPU的執(zhí)行權,如果沒記錄程序執(zhí)行的位置,等下次搶到CPU執(zhí)行權的時候,這尼瑪咋弄? 重新開始執(zhí)行嗎?
顯然是不行的,所以需要程序計數(shù)器來給每個線程的執(zhí)行到的行號做下標記。各個現(xiàn)場的程序計數(shù)器互不影響,獨立存儲。
我們來看看javap -c 處理的反匯編
簡單理解,可以理解為上面的行號, 實際上存儲的是這行代碼對應在內存中的指針位置。
字節(jié)碼 由誰來執(zhí)行? 肯定是字節(jié)碼執(zhí)行引擎嘛 ,所以 字節(jié)碼執(zhí)行引擎肯定知道程序的執(zhí)行位置,這樣 字節(jié)碼執(zhí)行引擎和程序計數(shù)器就關聯(lián)起來了。
本地方法棧
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 顯示正在處理的類的系統(tǒng)信息 (路徑, 大小, 日期, 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_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: new #2 // class com/gof/test/Artisan3: dup4: invokespecial #3 // Method "<init>":()V7: astore_18: aload_19: invokevirtual #4 // Method doSomething:()I12: pop13: returnpublic int doSomething();Code:0: iconst_11: istore_12: iconst_23: istore_24: iload_15: iload_26: iadd7: bipush 109: imul10: istore_311: iload_312: ireturn }JVM字節(jié)碼指令集手冊
下載地址戳這里–>提取碼:9ru5
總結
以上是生活随笔為你收集整理的JVM - 结合代码示例彻底搞懂Java内存区域_线程栈 | 本地方法栈 | 程序计数器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设计模式 -结构型模式_ 装饰者模式De
- 下一篇: JVM - 结合代码示例彻底搞懂Java