圖解JVM內(nèi)存模型
前言
上篇文章我們一起了解了jvm虛擬機類的加載機制,而且是以一種純大白話進行的一場閑聊,相信小伙伴們應(yīng)該印象深刻,感興趣的小伙伴可以重溫一下上一篇文章大白話談JVM的類加載機制。
當(dāng)jvm加載了類后,會把需要使用的對象放入到內(nèi)存當(dāng)中,那么jvm的內(nèi)存模型是什么樣的呢?
今天我們就來探索一下jvm的內(nèi)存模型。由于有小伙伴反映想加些圖更容易理解,王子接下來的文章打算用更多的圖例來講解。
方法區(qū)
很多小伙伴之前也了解過jvm的內(nèi)存模型,知道有方法區(qū)這個東西,但可能了解的不是很詳細。
其實方法區(qū)是在JDK1.8以前的版本里存在的一塊內(nèi)存區(qū)域,主要就是存放從class文件里加載進來的類的,而且常量池也是在這塊區(qū)域內(nèi)的。
但是在JDK1.8之后,這塊區(qū)域搖身一變,換了名字,叫做“Metaspace”,翻譯過來就是“元數(shù)據(jù)空間”的意思。當(dāng)然它只是改了個名,實現(xiàn)的功能是沒變的。
程序計數(shù)器
假設(shè)我們的代碼是這樣的:
public class Main { public static void main(String[] args) { SysUser sysUser = new SysUser(); sysUser.setAvatar("1"); } }
這個是我們的java代碼,是面向我們開發(fā)者的,然后會編譯成class字節(jié)碼文件,在class字節(jié)碼文件中存放的是一條條的字節(jié)碼命令,他對應(yīng)了一條條的機器指令,計算機只有讀到機器指令才知道它要干什么。
所以當(dāng)JVM加載類信息后,實際上就是使用字節(jié)碼執(zhí)行引擎去執(zhí)行我們的代碼編譯出來的一條條字節(jié)碼指令,如下圖。
那么在執(zhí)行字節(jié)碼指令的時候,jvm是怎么知道該執(zhí)行哪條指令了呢?這時候程序計數(shù)器就出現(xiàn)了。
它就是用來記錄當(dāng)前執(zhí)行的字節(jié)碼指令位置的。
另外,小伙伴們都知道,JVM是支持多線程的,所以如果我們開啟了多線程,就會有多個線程在執(zhí)行不同的字節(jié)碼指令,為了他們之間的字節(jié)碼指令不會混在一起,所以每個線程都會有自己的程序計數(shù)器,用來記錄每個線程自己的指令現(xiàn)在執(zhí)行到哪一條了,如下圖:
JAVA虛擬機棧
我們現(xiàn)在知道,jvm執(zhí)行class中指令時是通過程序計數(shù)器來鎖定執(zhí)行的指令位置的,但是在我們執(zhí)行的方法里,會有很多的局部變量等數(shù)據(jù),虛擬機棧就是用來保存方法的局部變量的,而且每個線程都會有自己的虛擬機棧,比如我們之前的代碼:
public class Main { public static void main(String[] args) { SysUser sysUser = new SysUser(); sysUser.setAvatar("1"); } }
這個代碼會啟動一個main線程,并把局部變量sysUser保存到棧中。
如果線程執(zhí)行了一個方法,就會對這個方法調(diào)用創(chuàng)建一個棧幀,然后就是所謂的壓棧操作(先進后出),如下:
然后我們代碼繼續(xù)執(zhí)行,調(diào)用了setAvatar方法,那么就會繼續(xù)創(chuàng)建棧幀,如下:
當(dāng)setAcatar方法執(zhí)行完畢,就會對方法的棧幀執(zhí)行出棧操作。
以上就是JAVA虛擬機棧這一部分的作用,簡單概括就是:調(diào)用方法就創(chuàng)建棧幀,壓棧,方法執(zhí)行完就執(zhí)行出棧操作。
JAVA堆內(nèi)存
說完了java虛擬機棧,那我們再來說一個很重要的內(nèi)存區(qū)域java堆內(nèi)存,它是用來存放我們代碼中創(chuàng)建的各種對象的。
還是以剛才的代碼為例,當(dāng)我們執(zhí)行new SysUser()的時候,就創(chuàng)建了一個SysUser實例對象,而這個對象本身又會有很多的屬性和方法,這樣的實例化對象的數(shù)據(jù)就是存放在堆內(nèi)存中的。
而這個時候我們在棧中存儲的局部變量實際上存的就是這個對象的內(nèi)存地址,也可以理解為一個引用地址。如下圖:
到這里JVM的內(nèi)存區(qū)域已經(jīng)和小伙伴們介紹完了,給大家來一張整體的內(nèi)存區(qū)域圖,以便理解:
其他內(nèi)存區(qū)域
除了前文我們介紹的內(nèi)存區(qū)域,jdk的api中(io、nio、socket)相關(guān),其實它們的內(nèi)部已經(jīng)不是java代碼了,而是調(diào)用了native方法調(diào)用了本地操作系統(tǒng)的一些方法,可能是c語言編寫的或者是一些底層類庫。
在調(diào)用native方法的時候,線程就會對應(yīng)本地方法棧,這個是于java虛擬機棧類似的東東,放的就是native方法的各種局部變量表。
除此之外還有一個區(qū)域,是不屬于JVM的,通過NIO的allocateDirect的api,可以在堆外分配內(nèi)存空間,從而直接操作堆外的內(nèi)存空間數(shù)據(jù)。
有些場景下,堆外內(nèi)存空間會提升性能,這個問題我們之后再逐步探索,今天就不說這個了。
總結(jié)
本文到這里就結(jié)束啦,王子采納了一些小伙伴的意見,畫了很多圖有助于小伙伴們更好的理解,希望能夠幫到大家。
那我們下篇文章見。
以上就是圖解JVM內(nèi)存模型的詳細內(nèi)容,更多關(guān)于JVM內(nèi)存模型的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Druid連接池未關(guān)閉導(dǎo)致內(nèi)存泄漏問題
這篇文章主要介紹了Druid連接池未關(guān)閉導(dǎo)致內(nèi)存泄漏問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12如何解決java:找不到符號符號:類__(使用了lombok的注解)
在使用IntelliJ IDEA開發(fā)Java項目時,可能遇到通過@lombok注解自動生成get和set方法不生效的問題,解決這一問題需要幾個步驟,首先,確認Lombok插件已在IDEA中安裝并啟用,其次,確保項目中已添加Lombok的依賴,對于Maven和Gradle項目2024-10-10Spring Boot中如何使用Convert接口實現(xiàn)類型轉(zhuǎn)換器
這篇文章主要介紹了Spring Boot中使用Convert接口實現(xiàn)類型轉(zhuǎn)換器的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Lombok注解之@SuperBuilder--解決無法builder父類屬性問題
這篇文章主要介紹了Lombok注解之@SuperBuilder--解決無法builder父類屬性問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09Java super關(guān)鍵字用法實戰(zhàn)案例分析
這篇文章主要介紹了Java super關(guān)鍵字用法,結(jié)合具體案例形式分析了java super關(guān)鍵字調(diào)用父類構(gòu)造方法、屬性及方法等相關(guān)操作技巧與注意事項,需要的朋友可以參考下2019-09-09