
類裝載器ClassLoader
負(fù)責(zé)加載class文件,class文件在文件開頭有特定的文件標(biāo)示,并且ClassLoader只負(fù)責(zé)class文件的加載,至于它是否可以運(yùn)行,則由Execution Engine決定

-
類裝載器的分類:
啟動(dòng)類加載器(Bootstrap)C++
擴(kuò)展類加載器(Extension)Java
應(yīng)用程序類加載器(AppClassLoader)Java,也叫系統(tǒng)類加載器,加載當(dāng)前應(yīng)用的classpath的所有類
用戶自定義加載器 Java.lang.ClassLoader的子類,用戶可以定制類的加載方式
image.png
執(zhí)行引擎
Execution Engine執(zhí)行引擎負(fù)責(zé)解釋命令,提交操作系統(tǒng)執(zhí)行。
Native Interface本地接口
??本地接口的作用是融合不同的編程語言為 Java 所用,它的初衷是融合 C/C++程序,Java 誕生的時(shí)候是 C/C++橫行的時(shí)候,要想立足,必須有調(diào)用 C/C++程序,于是就在內(nèi)存中專門開辟了一塊區(qū)域處理標(biāo)記為native的代碼,它的具體做法是 Native Method Stack中登記 native方法,在Execution Engine 執(zhí)行時(shí)加載native libraies。
Native Method Stack
??加載Native方法
PC寄存器
??每個(gè)線程都有一個(gè)程序計(jì)數(shù)器,是線程私有的,就是一個(gè)指針,指向方法區(qū)中的方法字節(jié)碼(用來存儲(chǔ)指向下一條指令的地址,也即將要執(zhí)行的指令代碼),由執(zhí)行引擎讀取下一條指令,是一個(gè)非常小的內(nèi)存空間,幾乎可以忽略不記。
Method Area 方法區(qū)
??方法區(qū)是被所有線程共享,所有字段和方法字節(jié)碼,以及一些特殊方法如構(gòu)造函數(shù),接口代碼也在此定義。簡(jiǎn)單說,所有定義的方法的信息都保存在該區(qū)域,此區(qū)屬于共享區(qū)間。 靜態(tài)變量+常量+類信息(構(gòu)造方法/接口定義)+運(yùn)行時(shí)常量池存在方法區(qū)中,但是實(shí)例變量存在堆內(nèi)存中,和方法區(qū)無關(guān)。
??介紹方法區(qū)不得不說的就是運(yùn)行時(shí)常量池,它是方法區(qū)的一部分。Class文件中除了有類、字段、方法、接口等描述信息外,還有一個(gè)信息是常量池,常量池主要用來存放編譯期生成的各種字面量和符號(hào)引用,這部分的內(nèi)容將在類加載后存放到運(yùn)行時(shí)常量池中。
??運(yùn)行時(shí)常量池相比于Class文件常量池的一個(gè)重要特性就是具備動(dòng)態(tài)性,Java并不要求常量一定只能在編譯其產(chǎn)生,也可以在運(yùn)行期間將新的常量放入常量池中,這種特性最明顯的使用方法就是String.intern方法。
??許多熟悉GC的程序員們更喜歡把方法區(qū)稱之為永久代,但是本質(zhì)上它倆其實(shí)是不等價(jià)的,僅僅是因?yàn)镠otSpot虛擬機(jī)的設(shè)計(jì)團(tuán)隊(duì)將GC分代收集擴(kuò)展到了方法區(qū)。相對(duì)來說,垃圾收集行為在這個(gè)區(qū)域是比較少出現(xiàn)的,這個(gè)區(qū)域的垃圾收集目標(biāo)主要是針對(duì)常量池的回收和類型的卸載。
Stack 棧
- 棧是什么?
??棧也叫棧內(nèi)存,主管Java程序的運(yùn)行,是在線程創(chuàng)建時(shí)創(chuàng)建,它的生命期是跟隨線程的生命期,線程結(jié)束棧內(nèi)存也就釋放,對(duì)于棧來說不存在垃圾回收問題,只要線程一結(jié)束該棧就Over,生命周期和線程一致,是線程私有的。8種基本類型的變量+對(duì)象的引用變量+實(shí)例方法都是在函數(shù)的棧內(nèi)存中分配。 - 棧幀中主要保存3 類數(shù)據(jù):
本地變量(Local Variables):輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量;
棧操作(Operand Stack):記錄出棧、入棧的操作;
棧幀數(shù)據(jù)(Frame Data):包括類文件、方法等等。 - 棧運(yùn)行原理:
??棧中的數(shù)據(jù)都是以棧幀(Stack Frame)的格式存在,棧幀是一個(gè)內(nèi)存區(qū)塊,是一個(gè)數(shù)據(jù)集,是一個(gè)有關(guān)方法(Method)和運(yùn)行期數(shù)據(jù)的數(shù)據(jù)集,當(dāng)一個(gè)方法A被調(diào)用時(shí)就產(chǎn)生了一個(gè)棧幀 F1,并被壓入到棧中,A方法又調(diào)用了 B方法,于是產(chǎn)生棧幀 F2 也被壓入棧,B方法又調(diào)用了 C方法,于是產(chǎn)生棧幀 F3 也被壓入棧,
……
執(zhí)行完畢后,先彈出F3棧幀,再?gòu)棾鯢2棧幀,再?gòu)棾鯢1棧幀……
遵循“先進(jìn)后出”/“后進(jìn)先出”原則。
Heap 堆(jdk1.7之前)
?? 一個(gè)JVM實(shí)例只存在一個(gè)堆內(nèi)存,堆內(nèi)存的大小是可以調(diào)節(jié)的。類加載器讀取了類文件后,需要把類、方法、常變量放到堆內(nèi)存中,保存所有引用類型的真實(shí)信息,以方便執(zhí)行器執(zhí)行,堆內(nèi)存在邏輯上分為三部分:

注意:伊甸區(qū):0區(qū):1區(qū) = 8 :1 :1(比例大?。?/p>
- 新生區(qū):
??新生區(qū)是類的誕生、成長(zhǎng)、消亡的區(qū)域,一個(gè)類在這里產(chǎn)生,應(yīng)用,最后被垃圾回收器收集,結(jié)束生命。新生區(qū)又分為兩部分: 伊甸區(qū)(Eden space)和幸存者區(qū)(Survivor pace) ,所有的類都是在伊甸區(qū)被new出來的。幸存區(qū)有兩個(gè): 0區(qū)(Survivor 0 space)和1區(qū)(Survivor 1 space)。當(dāng)伊甸園的空間用完時(shí),程序又需要?jiǎng)?chuàng)建對(duì)象,JVM的垃圾回收器將對(duì)伊甸園區(qū)進(jìn)行垃圾回收(Minor GC),將伊甸園區(qū)中的不再被其他對(duì)象所引用的對(duì)象進(jìn)行銷毀。然后將伊甸園中的剩余對(duì)象移動(dòng)到幸存 0區(qū)。若幸存 0區(qū)也滿了,再對(duì)該區(qū)進(jìn)行垃圾回收,然后移動(dòng)到 1 區(qū)。那如果1 區(qū)也滿了呢?再次垃圾回收,滿足條件后再移動(dòng)到養(yǎng)老區(qū)。若養(yǎng)老區(qū)也滿了,那么這個(gè)時(shí)候?qū)a(chǎn)生MajorGC(FullGC),進(jìn)行養(yǎng)老區(qū)的內(nèi)存清理。若養(yǎng)老區(qū)執(zhí)行了Full GC之后發(fā)現(xiàn)依然無法進(jìn)行對(duì)象的保存,就會(huì)產(chǎn)生OOM異?!癘utOfMemoryError”。
??假設(shè)0區(qū)就是 from space;1區(qū)就是 to space:
??Minor GC:將from區(qū)和伊甸園區(qū)中不再被其他對(duì)象所引用的對(duì)象進(jìn)行銷毀,把伊甸園區(qū)和from區(qū)中的幸存者從都移動(dòng)到to 區(qū),此時(shí)伊甸園區(qū)和from區(qū)變?yōu)榭?,然后把form區(qū)和to區(qū)相互轉(zhuǎn)換,所以to區(qū)總為空。
每移動(dòng)一次,幸存者的年齡就加一,當(dāng)年齡大于15的時(shí)候,再將幸存者移動(dòng)到養(yǎng)老區(qū)。 - 養(yǎng)老區(qū):
??滿足條件后再移動(dòng)到養(yǎng)老區(qū)。若養(yǎng)老區(qū)也滿了,那么這個(gè)時(shí)候?qū)a(chǎn)生MajorGC(FullGC),進(jìn)行養(yǎng)老區(qū)的內(nèi)存清理。若養(yǎng)老區(qū)執(zhí)行了Full GC之后發(fā)現(xiàn)依然無法進(jìn)行對(duì)象的保存,就會(huì)產(chǎn)生OOM異?!癘utOfMemoryError”。
??如果出現(xiàn)java.lang.OutOfMemoryError: Java heap space異常,說明Java虛擬機(jī)的堆內(nèi)存不夠。
??原因有二:
(1)Java虛擬機(jī)的堆內(nèi)存設(shè)置不夠,可以通過參數(shù)-Xms、-Xmx來調(diào)整。
(2)代碼中創(chuàng)建了大量大對(duì)象,并且長(zhǎng)時(shí)間不能被垃圾收集器收集(存在被引用)。
永久區(qū)
??永久存儲(chǔ)區(qū)是一個(gè)常駐內(nèi)存區(qū)域,用于存放JDK自身所攜帶的 Class,Interface 的元數(shù)據(jù),也就是說它存儲(chǔ)的是運(yùn)行環(huán)境必須的類信息,被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會(huì)被垃圾回收器回收掉的,關(guān)閉 JVM 才會(huì)釋放此區(qū)域所占用的內(nèi)存。
如果出現(xiàn)java.lang.OutOfMemoryError: PermGen space,說明是Java虛擬機(jī)對(duì)永久代Perm內(nèi)存設(shè)置不夠。一般出現(xiàn)這種情況,都是程序啟動(dòng)需要加載大量的第三方j(luò)ar包。例如:在一個(gè)Tomcat下部署了太多的應(yīng)用。或者大量動(dòng)態(tài)反射生成的類不斷被加載,最終導(dǎo)致Perm區(qū)被占滿。
Jdk1.6及之前: 有永久代, 常量池1.6在方法區(qū)
Jdk1.7: 有永久代,但已經(jīng)逐步“去永久代”,常量池1.7在堆
Jdk1.8及之后: 無永久代,常量池1.8在元空間 >
JDK 1.8之后將最初的永久代取消了,由元空間取代。

演示:
使用jdk1.7

使用jdk1.8

