JVM整體組成
- 類加載器
- 執(zhí)行引擎
- 運行時數(shù)據(jù)區(qū)
- 本地接口
??程序在執(zhí)行之前先要把java代碼轉(zhuǎn)換成字節(jié)碼(class文件),jvm首先需要把字節(jié)碼通過一定的方式 類加載器(ClassLoader) 把文件加載到內(nèi)存中 運行時數(shù)據(jù)區(qū)(Runtime Data Area) 塞俱,而字節(jié)碼文件是jvm的一套指令集規(guī)范,并不能直接交個底層操作系統(tǒng)去執(zhí)行,因此需要特定的命令解析器 執(zhí)行引擎(Execution Engine) 將字節(jié)碼翻譯成底層系統(tǒng)指令再交由CPU去執(zhí)行在扰,而這個過程中需要調(diào)用其他語言的接口 本地庫接口(Native Interface) 來實現(xiàn)整個程序的功能,這就是這4個主要組成部分的職責(zé)與功能雷客。
概念
- JVM內(nèi)存結(jié)構(gòu):和Java虛擬機的運行時區(qū)域有關(guān)芒珠。
- Java內(nèi)存模型:和Java的并發(fā)編程有關(guān)。就是一種符合內(nèi)存模型規(guī)范的搅裙,屏蔽了各種硬件和操作系統(tǒng)的訪問差異的皱卓,保證了Java程序在各種平臺下對內(nèi)存的訪問都能保證效果一致的機制及規(guī)范。目的是解決由于多線程通過共享內(nèi)存進(jìn)行通信時部逮,存在的本地內(nèi)存數(shù)據(jù)不一致娜汁、編譯器會對代碼指令重排序、處理器會對代碼亂序執(zhí)行等帶來的問題兄朋。
- Java對象模型:和Java對象在虛擬機中的表現(xiàn)形式有關(guān)掐禁。Java是一種面向?qū)ο蟮恼Z言,而Java對象在JVM中的存儲也是有一定的結(jié)構(gòu)的颅和。而這個關(guān)于Java對象自身的存儲模型稱之為Java對象模型傅事。
JVM運行時數(shù)據(jù)區(qū)域
程序計數(shù)器(PC 寄存器)
- 簡述:
??這塊內(nèi)存區(qū)域很小,它是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器峡扩,字節(jié)碼解釋器通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令蹭越。
??如果線程正在執(zhí)行的是一個Java方法這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址,如果當(dāng)前線程正在執(zhí)行的是一個本地方法有额,那么此時程序計數(shù)器為Undefined。
- 作用:
1、字節(jié)碼解釋器通過改變程序計數(shù)器來依次讀取指令巍佑,從而實現(xiàn)代碼的流程控制茴迁。
2、在多線程情況下萤衰,程序計數(shù)器記錄的是當(dāng)前線程執(zhí)行的位置堕义,從而當(dāng)線程切換回來時,就知道上次線程執(zhí)行到哪了脆栋。
- 特點:
1倦卖、是一塊較小的內(nèi)存空間。
2椿争、線程私有怕膛,每條線程都有自己的程序計數(shù)器。
3秦踪、生命周期:隨著線程的創(chuàng)建而創(chuàng)建褐捻,隨著線程的結(jié)束而銷毀。
4椅邓、是唯一一個不會出現(xiàn) OutOfMemoryError的內(nèi)存區(qū)域柠逞。
- 異常規(guī)定:無
??如果線程正在執(zhí)行Java中的方法,程序計數(shù)器記錄的就是正在執(zhí)行虛擬機字節(jié)碼指令的地址景馁,如果是Native方法板壮,這個計數(shù)器就為空(undefined),因此該內(nèi)存區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定OutOfMemoryError的區(qū)域合住。
Java 虛擬機棧(Java 棧)
- 簡述:Java 虛擬機棧是描述 Java 方法運行過程的內(nèi)存模型绰精。它的生命周期與線程相同。
??Java 虛擬機棧會為每一個即將運行的 Java 方法創(chuàng)建一塊叫做“棧幀”的區(qū)域聊疲,用于存放該方法運行過程中的一些信息茬底,如:局部變量表、操作數(shù)棧获洲、動態(tài)鏈接阱表、方法出口等信息,每一個方法從調(diào)用直至執(zhí)行完畢的過程贡珊,就對應(yīng)著一個棧幀在虛擬機中入棧到出棧的過程最爬。
- 壓棧出棧過程
??當(dāng)方法運行過程中需要創(chuàng)建局部變量時,就將局部變量的值存入棧幀中的局部變量表中门岔。Java 虛擬機棧的棧頂?shù)臈钱?dāng)前正在執(zhí)行的活動棧爱致,也就是當(dāng)前正在執(zhí)行的方法,PC 寄存器也會指向這個地址寒随。只有這個活動的棧幀的本地變量可以被操作數(shù)棧使用糠悯,當(dāng)在這個棧幀中調(diào)用另一個方法帮坚,與之對應(yīng)的棧幀又會被創(chuàng)建,新創(chuàng)建的棧幀壓入棧頂互艾,變?yōu)楫?dāng)前的活動棧幀试和。方法結(jié)束后,當(dāng)前棧幀被移出纫普,棧幀的返回值變成新的活動棧幀中操作數(shù)棧的一個操作數(shù)阅悍。如果沒有返回值,那么新的活動棧幀中操作數(shù)棧的操作數(shù)沒有變化昨稼。
- 特點:
1节视、局部變量表隨著棧幀的創(chuàng)建而創(chuàng)建,它的大小在編譯時確定假栓,創(chuàng)建時只需分配事先規(guī)定的大小即可寻行。在方法運行過程中,局部變量表的大小不會發(fā)生改變但指。
2寡痰、Java 虛擬機棧會出現(xiàn)兩種異常:StackOverFlowError 和 OutOfMemoryError。
3棋凳、StackOverFlowError 若 Java 虛擬機棧的大小不允許動態(tài)擴展拦坠,那么當(dāng)線程請求棧的深度超過當(dāng)前 Java 虛擬機棧的最大深度時,拋出 StackOverFlowError 異常剩岳。
4贞滨、OutOfMemoryError 若允許動態(tài)擴展,那么當(dāng)線程請求棧時內(nèi)存用完了拍棕,無法再動態(tài)擴展時晓铆,拋出 OutOfMemoryError 異常。
5绰播、Java 虛擬機棧也是線程私有骄噪,隨著線程創(chuàng)建而創(chuàng)建,隨著線程的結(jié)束而銷毀蠢箩。
- 異常規(guī)定:StackOverflowError链蕊、OutOfMemoryError
1、如果線程請求的棧深度大于虛擬機所允許的棧深度就會拋出StackOverflowError異常谬泌。
2滔韵、如果虛擬機是可以動態(tài)擴展的,如果擴展時無法申請到足夠的內(nèi)存就會拋出OutOfMemoryError異常掌实。
本地方法棧(C 棧)
- 簡述:
??本地方法棧是為 JVM 運行 Native 方法準(zhǔn)備的空間陪蜻,由于很多 Native 方法都是用 C 語言實現(xiàn)的,所以它通常又叫 C 棧贱鼻。它與 Java 虛擬機棧實現(xiàn)的功能類似宴卖,只不過本地方法棧是描述本地方法運行過程的內(nèi)存模型滋将。特性和異常: 同虛擬機棧一樣。
- 棧幀變化過程
??本地方法被執(zhí)行時症昏,在本地方法棧也會創(chuàng)建一塊棧幀耕渴,用于存放該方法的局部變量表、操作數(shù)棧齿兔、動態(tài)鏈接、方法出口信息等础米。方法執(zhí)行結(jié)束后分苇,相應(yīng)的棧幀也會出棧,并釋放內(nèi)存空間屁桑。也會拋出 StackOverFlowError 和 OutOfMemoryError 異常医寿。
??如果 Java 虛擬機本身不支持 Native 方法,或是本身不依賴于傳統(tǒng)棧蘑斧,那么可以不提供本地方法棧靖秩。如果支持本地方法棧,那么這個棧一般會在線程創(chuàng)建的時候按線程分配竖瘾。
堆
- 簡述:
?? 堆是用來存放對象的內(nèi)存空間沟突,幾乎所有的對象都存儲在堆中。不同的區(qū)域存放不同生命周期的對象捕传,這樣可以根據(jù)不同的區(qū)域使用不同的垃圾回收算法惠拭,更具有針對性。
- 特點:
1庸论、線程共享职辅,整個 Java 虛擬機只有一個堆,所有的線程都訪問同一個堆聂示。而程序計數(shù)器域携、Java 虛擬機棧、本地方法棧都是一個線程對應(yīng)一個鱼喉。
2秀鞭、在虛擬機啟動時創(chuàng)建。
3蒲凶、是垃圾回收的主要場所气筋。
4、進(jìn)一步可分為:新生代(Eden區(qū) From Survior區(qū)旋圆、 To Survivor區(qū))宠默、老年代。
5灵巧、從內(nèi)存分配角度來看搀矫,線程共享的 Java 堆中可能劃分出多個線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer抹沪,TLAB)??堆的大小既可以固定也可以擴展,但對于主流的虛擬機瓤球,堆的大小是可擴展的融欧,因此當(dāng)線程請求分配內(nèi)存,但堆已滿卦羡,且內(nèi)存已無法再擴展時噪馏,就拋出 OutOfMemoryError 異常。
??Java 堆所使用的內(nèi)存不需要保證是連續(xù)的绿饵。而由于堆是被所有線程共享的欠肾,所以對它的訪問需要注意同步問題,方法和對應(yīng)的屬性都需要保證一致性拟赊。
- 異常規(guī)定:OutOfMemoryError
??如果在堆中沒有內(nèi)存完成實例分配刺桃,并且堆不可以再擴展時,將會拋出OutOfMemoryError吸祟。
??Java虛擬機規(guī)范規(guī)定瑟慈,Java堆可以處在物理上不連續(xù)的內(nèi)存空間中,只要邏輯上連續(xù)即可屋匕,就像我們的磁盤空間一樣葛碧。在實現(xiàn)上也可以是固定大小的,也可以是可擴展的过吻,不過當(dāng)前主流的虛擬機都是可擴展的吹埠,通過-Xmx和-Xms控制。
方法區(qū)
簡述:
??Java 虛擬機規(guī)范中定義方法區(qū)是堆的一個邏輯部分疮装。方法區(qū)存放以下信息:
??已經(jīng)被虛擬機加載的類信息缘琅,常量,靜態(tài)變量廓推,即時編譯器編譯后的代碼特點:
1刷袍、線程共享。 方法區(qū)是堆的一個邏輯部分樊展,因此和堆一樣呻纹,都是線程共享的。整個虛擬機中只有一個方法區(qū)专缠。
2雷酪、永久代。 方法區(qū)中的信息一般需要長期存在涝婉,而且它又是堆的邏輯分區(qū)哥力,因此用堆的劃分方法,把方法區(qū)稱為“永久代”。HotSpot中吩跋,方法區(qū)≈永久代寞射。不過JDK 7之后,我們使用的HotSpot應(yīng)該就沒有永久代這個概念了锌钮,會采用Native Memory來實現(xiàn)方法區(qū)的規(guī)劃了桥温。
3、內(nèi)存回收效率低梁丘。 方法區(qū)中的信息一般需要長期存在侵浸,回收一遍之后可能只有少量信息無效。主要回收目標(biāo)是:對常量池的回收氛谜;對類型的卸載通惫。
4、Java 虛擬機規(guī)范對方法區(qū)的要求比較寬松混蔼。 和堆一樣,允許固定大小珊燎,也允許動態(tài)擴展惭嚣,還允許不實現(xiàn)垃圾回收。
- 異常規(guī)定:OutOfMemoryError
運行時常量池
??它是方法區(qū)的一部分悔政。Class文件中除了有類的版本信息晚吞、字段、方法谋国、接口等描述信息外槽地,還有一項信息就是常量池,用于存放編譯期間生成的各種字面量和符號引用芦瘾,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運行時常量池中捌蚊,另外翻譯出來的直接引用也會存儲在這個區(qū)域中。
直接內(nèi)存
??直接內(nèi)存是除 Java 虛擬機之外的內(nèi)存近弟,但也可能被 Java 使用缅糟。
??在 NIO 中引入了一種基于通道和緩沖的 IO 方式。它可以通過調(diào)用本地方法直接分配 Java 虛擬機之外的內(nèi)存祷愉,然后通過一個存儲在堆中的 DirectByteBuffer對象直接操作該內(nèi)存窗宦,而無須先將外部內(nèi)存中的數(shù)據(jù)復(fù)制到堆中再進(jìn)行操作,從而提高了數(shù)據(jù)操作的效率二鳄。
??直接內(nèi)存的大小不受 Java 虛擬機控制赴涵,但既然是內(nèi)存,當(dāng)內(nèi)存不足時就會拋出 OutOfMemoryError 異常订讼。
直接內(nèi)存與堆內(nèi)存的比較
- 直接內(nèi)存申請空間耗費更高的性能
- 直接內(nèi)存讀取IO的性能要優(yōu)于普通的堆內(nèi)存
- 直接內(nèi)存作用鏈:本地IO->直接內(nèi)存->本地IO
- 堆內(nèi)存作用鏈:本地IO->直接內(nèi)存->非直接內(nèi)存->直接內(nèi)存->本地IO