JVM - 结合代码示例彻底搞懂Java内存区域_线程栈 | 本地方法栈 | 程序计数器
文章目錄
- Pre
- 運(yùn)行時(shí)數(shù)據(jù)區(qū)總覽
- 線程棧
- 概要
- 棧內(nèi)部主要組成部分
- 局部變量
- 操作數(shù)棧
- 動(dòng)態(tài)鏈接
- 方法出口
- 小結(jié)
- 程序計(jì)數(shù)器
- 本地方法棧
- 附
- 測(cè)試demo
- javap
- JVM字節(jié)碼指令集手冊(cè)
Pre
JVM-01Java內(nèi)存區(qū)域與內(nèi)存溢出異常(上)【運(yùn)行時(shí)區(qū)域數(shù)據(jù)】
JVM-02內(nèi)存區(qū)域與內(nèi)存溢出異常(中)【hotspot虛擬機(jī)對(duì)象】
JVM-03內(nèi)存區(qū)域與內(nèi)存溢出異常(下)【OutOfMemoryError案例】
運(yùn)行時(shí)數(shù)據(jù)區(qū)總覽
字節(jié)碼文件被裝載子系統(tǒng)裝載到JVM中,字節(jié)碼執(zhí)行引擎負(fù)責(zé)執(zhí)行這些字節(jié)碼文件。
裝載子系統(tǒng)和執(zhí)行引擎都是C++的實(shí)現(xiàn)。
裝載子系統(tǒng): JVM-白話聊一聊JVM類(lèi)加載和雙親委派機(jī)制源碼解析
我們重點(diǎn)關(guān)注下運(yùn)行時(shí)數(shù)據(jù)區(qū)域 ,先關(guān)注線程私有的這3個(gè)部分。
線程棧
概要
沒(méi)給方法被執(zhí)行的時(shí)候,JVM都會(huì)同步創(chuàng)建一個(gè)棧幀。
這個(gè)棧和數(shù)據(jù)結(jié)構(gòu)的棧結(jié)構(gòu)是一樣的, FILO .
舉個(gè)例子 ,方法A 中調(diào)用了方法B , 代碼先執(zhí)行方法A ,此時(shí)方法A 入棧, 然后調(diào)用方法B,這個(gè)時(shí)候方法B入棧 。 當(dāng)方法B執(zhí)行結(jié)束后,方法B出棧,回到方法A執(zhí)行的地方,方法A繼續(xù)執(zhí)行,執(zhí)行結(jié)束 ,方法A出棧。
棧內(nèi)部主要組成部分
【Java代碼】
public int doSomething() {int a = 1 ;int b = 2 ;int c = (a + b) * 10 ;return c;}【javap -c 反匯編】
如何操作的,見(jiàn)文末 ,JVM字節(jié)碼指令集也見(jiàn)文末
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類(lèi)型】
iconst_1 是個(gè)什么鬼? 查查操作手冊(cè)
istore_1
iconst_0 和 istore_0 是默認(rèn)存放調(diào)用該方法的對(duì)象。
這里就涉及到兩個(gè)組成部分 【局部變量】 + 【操作數(shù)棧】
局部變量
0x04 iconst_1 將 int 型 1 推送至棧頂 0x3c istore_1 將棧頂 int 型數(shù)值存入第二個(gè)本地變量 0x05 iconst_2 將 int 型 2 推送至棧頂 0x3d istore_2 將棧頂 int 型數(shù)值存入第三個(gè)本地變量比對(duì)代碼
int a = 1 ;int b = 2 ;iconst_1 , 將 int 1 壓入操作數(shù)棧 , istore_1 將棧頂 int 型數(shù)值存入第二個(gè)本地變量 ,這個(gè)時(shí)候 會(huì)先將 1 出棧,然后存入局部變量表。
istore_1 、istore_2 存入本地變量,就是放到了局部變量表。
操作數(shù)棧
0x04 iconst_1 將 int 型 1 推送至棧頂 0x05 iconst_2 將 int 型 2 推送至棧頂這兩步的意思 就是將代碼中的 a 和 b 對(duì)應(yīng)的值 1 和 2 壓入 操作數(shù)棧 。
這個(gè)操作數(shù)棧 本身也是棧結(jié)構(gòu), FILO . 入棧 出棧
繼續(xù)
int c = (a + b) * 10 ;return c; 4: iload_1 將第二個(gè) int 型本地變量推送至棧頂 ----> a的值 1 入棧 (操作數(shù)棧)5: iload_2 將第三個(gè) int 型本地變量推送至棧頂 ----> b的值 2 入棧 (操作數(shù)棧)6: iadd 將棧頂兩 int 型數(shù)值相加并將結(jié)果壓入棧頂 ----> 計(jì)算 a+b =3,結(jié)果壓入棧頂 (a ,b 出棧,計(jì)算結(jié)果,然后將a+b的結(jié)果壓入操作數(shù)棧)7: bipush 10 將單字節(jié)的常量值(-128~127)推送至棧頂 -----> 10 入棧 操作數(shù)棧)9: imul 將棧頂兩 int 型數(shù)值相乘并將結(jié)果壓入棧頂 ----> 計(jì)算乘積 3 * 10 ,將30壓入操作數(shù)棧10: istore_3 將棧頂 int 型數(shù)值存入第四個(gè)本地變量 ------> 給 c賦值 11: iload_3 將第四個(gè) int 型本地變量推送至棧頂 ----> 壓入操作數(shù)棧 12: ireturn 從當(dāng)前方法返回 int ----> 返回上面的行號(hào) 7 到9 ? 沒(méi)有9 。 我們這個(gè)常量10 也要占位置嘛 。
計(jì)算 結(jié)果肯定是CPU執(zhí)行的嘛 ,只不過(guò)數(shù)據(jù)是存放在內(nèi)存中的。
簡(jiǎn)言之,操作數(shù)棧,是程序運(yùn)行期間需要臨時(shí)存放數(shù)據(jù)的內(nèi)存空間。
動(dòng)態(tài)鏈接
符號(hào)引用 替換為 直接引用。
我們知道在類(lèi)裝載的過(guò)程中,有個(gè)過(guò)程是【解析】
JVM-白話聊一聊JVM類(lèi)加載和雙親委派機(jī)制源碼解析
舉個(gè)例子
public static void main(String[] args) {Artisan artisan = new Artisan();artisan.doSomething();}簡(jiǎn)單說(shuō)當(dāng)應(yīng)用執(zhí)行到artisan.doSomething()方法( 非靜態(tài)方法),需要找到這個(gè)方法在方法區(qū)的常量池中的具體的位置,這個(gè)過(guò)程就是動(dòng)態(tài)鏈接
方法區(qū)#運(yùn)行時(shí)常量池 ,是方法區(qū)的一部分。 Class文件中的常量池表用于存放編譯期間生成的各種字面量和符號(hào)引用,這部分內(nèi)容將在類(lèi)加載后放到方法區(qū)的運(yùn)行時(shí)常量池中。
方法出口
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í)行哇~
小結(jié)
程序計(jì)數(shù)器
簡(jiǎn)單理解,可以理解為 記錄程序執(zhí)行的位置。
線程私有。
Java多線程,當(dāng)線程A沒(méi)有搶到CPU的執(zhí)行權(quán),如果沒(méi)記錄程序執(zhí)行的位置,等下次搶到CPU執(zhí)行權(quán)的時(shí)候,這尼瑪咋弄? 重新開(kāi)始執(zhí)行嗎?
顯然是不行的,所以需要程序計(jì)數(shù)器來(lái)給每個(gè)線程的執(zhí)行到的行號(hào)做下標(biāo)記。各個(gè)現(xiàn)場(chǎng)的程序計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ)。
我們來(lái)看看javap -c 處理的反匯編
簡(jiǎn)單理解,可以理解為上面的行號(hào), 實(shí)際上存儲(chǔ)的是這行代碼對(duì)應(yīng)在內(nèi)存中的指針位置。
字節(jié)碼 由誰(shuí)來(lái)執(zhí)行? 肯定是字節(jié)碼執(zhí)行引擎嘛 ,所以 字節(jié)碼執(zhí)行引擎肯定知道程序的執(zhí)行位置,這樣 字節(jié)碼執(zhí)行引擎和程序計(jì)數(shù)器就關(guān)聯(lián)起來(lái)了。
本地方法棧
native 方法 底層C++的
附
測(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 類(lèi)型int a = 1 ;int b = 2 ;int c = (a + b) * 10 ;return c;} }javap
用法: javap <options> <classes> 其中, 可能的選項(xiàng)包括:-help --help -? 輸出此用法消息-version 版本信息-v -verbose 輸出附加信息-l 輸出行號(hào)和本地變量表-public 僅顯示公共類(lèi)和成員-protected 顯示受保護(hù)的/公共類(lèi)和成員-package 顯示程序包/受保護(hù)的/公共類(lèi)和成員 (默認(rèn))-p -private 顯示所有類(lèi)和成員-c 對(duì)代碼進(jìn)行反匯編-s 輸出內(nèi)部類(lèi)型簽名-sysinfo 顯示正在處理的類(lèi)的系統(tǒng)信息 (路徑, 大小, 日期, MD5 散列)-constants 顯示最終常量-classpath <path> 指定查找用戶類(lèi)文件的位置-cp <path> 指定查找用戶類(lèi)文件的位置-bootclasspath <path> 覆蓋引導(dǎo)類(lèi)文件的位置-c 對(duì)代碼進(jìn)行反匯編
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é)碼指令集手冊(cè)
下載地址戳這里–>提取碼:9ru5
總結(jié)
以上是生活随笔為你收集整理的JVM - 结合代码示例彻底搞懂Java内存区域_线程栈 | 本地方法栈 | 程序计数器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 设计模式 -结构型模式_ 装饰者模式De
- 下一篇: JVM - 结合代码示例彻底搞懂Java