前言
近幾個(gè)月學(xué)習(xí)了儒猿技術(shù)窩的專欄《從 0 開(kāi)始帶你成為JVM實(shí)戰(zhàn)高手》后,基于課程講解的知識(shí),做了提煉、歸納、擴(kuò)展,整理出當(dāng)前文章。
感謝專欄傳授的知識(shí)!專欄中從零開(kāi)始,一步一圖的方式,加上大量真實(shí)線上案例講解,讓我收獲很多。
一、內(nèi)存區(qū)域整體介紹
1 JDK1.7至JDK1.8的內(nèi)存區(qū)域演變

主要改變
元數(shù)據(jù)空間取代了方法區(qū),方法區(qū)是在虛擬機(jī)中,而元數(shù)據(jù)空間在本地內(nèi)存中。
2 JDK1.8內(nèi)存區(qū)域的詳細(xì)劃分

3.代碼執(zhí)行時(shí)的完整的流程
示例代碼

執(zhí)行流程

二、內(nèi)存區(qū)域劃分
1 方法區(qū)
JDK1.8以前叫方法區(qū),1.8以后叫"Metaspace"元數(shù)據(jù)空間.
(1)是否線程共享
線程共享。
(2)功能
存放已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
(3)JDK1.8的改動(dòng)
JDK1.8后,元空間替換了永久代,
主要改動(dòng)
元空間使用的是本地內(nèi)存,即不在JVM內(nèi)存區(qū)域中。
字符串常量由永久代轉(zhuǎn)移到堆中
和永久代相關(guān)的JVM參數(shù)已移除
可參考:
Metaspace 之一:Metaspace整體介紹(永久代被替換原因、元空間特點(diǎn)、元空間內(nèi)存查看分析方法)【非常好的文章】
2 程序計(jì)數(shù)器
(1)線程是否共享
線程獨(dú)享
(2)功能
JAVA代碼會(huì)被翻譯成字節(jié)碼,對(duì)應(yīng)各種字節(jié)碼指令。
如果線程正在執(zhí)行一個(gè)JAVA方法,計(jì)數(shù)器記錄當(dāng)前線程執(zhí)行到了哪一條字節(jié)碼指令;
如果是正在執(zhí)行的是Native方法,計(jì)數(shù)器的值為Undefined。
此內(nèi)存區(qū)域是唯一一個(gè)在JAVA虛擬機(jī)規(guī)范中沒(méi)有規(guī)定任何OutOfMemoryError情況的區(qū)域。
3 JAVA虛擬機(jī)棧
3.1 整體介紹
(1) 是否線程共享
線程獨(dú)享
(2) 功能
每當(dāng)線程啟動(dòng)的時(shí)候,就會(huì)分配一個(gè)Java虛擬機(jī)棧。
(3) JAVA虛擬機(jī)棧作用
執(zhí)行方法時(shí),會(huì)給方法創(chuàng)建棧幀然后入棧,在棧幀里存放這個(gè)方法對(duì)應(yīng)的局部變量之類的數(shù)據(jù)。
方法執(zhí)行完后,方法對(duì)應(yīng)的棧幀從虛擬機(jī)棧中出棧。(后面再深入學(xué)習(xí)入棧、出棧)
(4) 可能發(fā)生的錯(cuò)誤
StackOverflowError: 線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,JDK1.8默認(rèn)棧最大大小是1M。
OutOfMemoryError: 如果虛擬機(jī)棧可以動(dòng)態(tài)擴(kuò)展,而擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存。(后面再深入學(xué)這兩個(gè)Error)
3.1 棧幀
(1) 功能
線程執(zhí)行了一個(gè)方法,會(huì)對(duì)這個(gè)方法調(diào)用創(chuàng)建對(duì)應(yīng)的一個(gè)棧幀。
棧幀里包含這個(gè)方法的局部變量表、操作樹(shù)棧、動(dòng)態(tài)鏈接、方法出口。
局部變量表:存放了編譯期可知的各種基本類型(int、float等)、對(duì)象引用(指向堆內(nèi)存的地址)、returnAddress類型(指向了一條字節(jié)碼指令的地址)。
示例代碼中main線程執(zhí)行時(shí)的虛擬機(jī)棧內(nèi)存情況:

4 JAVA堆內(nèi)存
(1) 是否線程共享
線程共享。
(2) 功能
主要存放對(duì)象實(shí)例和數(shù)組。
可以物理上不連續(xù),但邏輯上要連續(xù)。
局部變量表的局部變量指向堆內(nèi)存的對(duì)象:

(3) 創(chuàng)建的對(duì)象在堆內(nèi)存中占用多少內(nèi)存?
一個(gè)對(duì)象對(duì)堆內(nèi)存空間的占用,大致分為兩塊:
(1)對(duì)象自己本身的一些信息
比如對(duì)象頭,在64位的Linux操作系統(tǒng)上,會(huì)占用16個(gè)字節(jié)。
(2)對(duì)象的實(shí)例變量作為數(shù)據(jù)占用的空間
各個(gè)實(shí)例變量占用的內(nèi)存,例如int類型占用4個(gè)字節(jié),long類型占用8個(gè)字節(jié)。還有數(shù)組、Map之類的會(huì)占用更多內(nèi)存。
5 本地方法棧
5.1 整體介紹
(1) 是否線程共享
線程獨(dú)享
(2) 功能
本地方法棧是為虛擬機(jī)使用到的Native方法服務(wù)。Java虛擬機(jī)棧是為執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)。
(3) 可能發(fā)生的錯(cuò)誤
和虛擬機(jī)棧一樣,也會(huì)有StackOverflowError和OutOfMemoryError錯(cuò)誤。
6 直接內(nèi)存
(1) 是否線程共享
線程共享
(2) 功能
為虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的部分。
在JDK1.4中新加入NIO(New Input/Output)類,引入了一種基于通道(Channel)和緩存(Buffer)的I/O方式,它可以使用Native函數(shù)庫(kù)直接分配堆外內(nèi)存,然后通過(guò)一個(gè)存儲(chǔ)在Java堆中的DirectByteBuffer對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作。
Netty框架就是使用了直接內(nèi)存。
(3) 作用
可以避免在Java堆和Native堆中來(lái)回的耗時(shí)操作。
(4) 可能發(fā)生的錯(cuò)誤
OutOfMemoryError: 會(huì)受到本機(jī)內(nèi)存限制,如果內(nèi)存區(qū)域總和大于物理內(nèi)存限制從而導(dǎo)致動(dòng)態(tài)擴(kuò)展時(shí)出現(xiàn)該異常。
參考:
Java虛擬機(jī)(JVM)你只要看這一篇就夠了! https://blog.csdn.net/qq_41701956/article/details/81664921