JVM的類加載過程以及雙親委派模型詳解
jvm 的主要組成部分
- 類加載器(ClassLoader)
- 運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)
- 執(zhí)行引擎(Execution Engine)
- 本地庫接口(Native Interface)
jvm 運(yùn)行時(shí)數(shù)據(jù)區(qū)的組成
方法區(qū):
①方法區(qū)主要用來存儲(chǔ)已被虛擬機(jī)加載的類信息(構(gòu)造器,接口定義)、常量、靜態(tài)變量和運(yùn)行時(shí)常量池等數(shù)據(jù)。
②該區(qū)域是被線程共享的。
③方法區(qū)里有一個(gè)運(yùn)行時(shí)常量池,用于存放靜態(tài)編譯產(chǎn)生的字面量和符號(hào)引用。該常量池具有動(dòng)態(tài)性,也就是說常量并不一定是編譯時(shí)確定,運(yùn)行時(shí)生成的常量也會(huì)存在這個(gè)常量池中。
虛擬機(jī)棧:
虛擬機(jī)棧也叫棧內(nèi)存,主管Java程序的運(yùn)行,是在線程創(chuàng)建時(shí)創(chuàng)建,它的生命期是跟隨線程的生命期,線程結(jié)束棧內(nèi)存也就釋放,對(duì)于棧來說不存在垃圾回收問題,只要線程一結(jié)束該棧就Over,生命周期和線程一致,是線程私有的。
8種基本類型的變量+對(duì)象的引用變量+實(shí)例方法都是在函數(shù)的棧內(nèi)存中分配。
①每個(gè)方法在執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接和方法出口等信息。
②虛擬機(jī)棧是線程私有的,它的生命周期與線程相同。
③局部變量表里存儲(chǔ)的是基本數(shù)據(jù)類型、returnAddress類型(指向一條字節(jié)碼指令的地址)和對(duì)象引用,這個(gè)對(duì)象引用有可能是指向?qū)ο笃鹗嫉刂返囊粋€(gè)指針,也有可能是代表對(duì)象的句柄或者與對(duì)象相關(guān)聯(lián)的位置。4.局部變量所需的內(nèi)存空間在編譯器間確定。
④操作數(shù)棧的作用主要用來存儲(chǔ)運(yùn)算結(jié)果以及運(yùn)算的操作數(shù),它不同于局部變量表通過索引來訪問,而是壓棧和出棧的方式
⑤每個(gè)棧幀都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧幀所屬方法的引用,持有這個(gè)引用是為了支持方法調(diào)用過程中的動(dòng)態(tài)連接.動(dòng)態(tài)鏈接就是將常量池中的符號(hào)引用在運(yùn)行期轉(zhuǎn)化為直接引用。
本地方法棧
本地方法棧和虛擬機(jī)棧類似,只不過本地方法棧為Native方法服務(wù)。
堆
java堆是所有線程所共享的一塊內(nèi)存,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,幾乎所有的對(duì)象實(shí)例都在這里創(chuàng)建,因此該區(qū)域經(jīng)常發(fā)生垃圾回收操作。
程序計(jì)數(shù)器
內(nèi)存空間小,字節(jié)碼解釋器工作時(shí)通過改變這個(gè)計(jì)數(shù)值可以選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理和線程恢復(fù)等功能都需要依賴這個(gè)計(jì)數(shù)器完成。該內(nèi)存區(qū)域是唯一一個(gè)java虛擬機(jī)規(guī)范沒有規(guī)定任何OOM(程序申請(qǐng)內(nèi)存過大,虛擬機(jī)無法滿足我們,然后自殺了)情況的區(qū)域。
程序在虛擬機(jī)中的執(zhí)行過程
首先是類加載器來加載class文件得到Class模板,放到方法區(qū)中(類的信息(構(gòu)造器,接口定義)、常量、靜態(tài)變量和運(yùn)行時(shí)常量池等數(shù)據(jù)),根據(jù)class模板來實(shí)例化對(duì)象的時(shí)候,會(huì)把對(duì)象放在堆中(可以提一下堆分代,垃圾回收策略,垃圾回收算法,內(nèi)存泄漏原因),根據(jù)對(duì)象調(diào)用方法時(shí),會(huì)將方法壓到棧中(8種基本類型的變量+對(duì)象的引用變量+實(shí)例方法),native方法會(huì)被壓入到本地方法棧中,由jvm向操作系統(tǒng)發(fā)送指令,由執(zhí)行引擎解釋命令發(fā)送給操作系統(tǒng),操作系統(tǒng)會(huì)調(diào)用本地方法接口,用本地方法庫,執(zhí)行本地方法。棧中的方法按照后進(jìn)先出的順序出棧,由程序計(jì)數(shù)器來指向下一個(gè)出棧的方法,棧中沒有垃圾回收,他們隨著線程的執(zhí)行完畢被釋放。
類加載的雙親委派模型
在介紹雙親委派模型之前先說下類加載器。對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立在 JVM 中的唯一性,每一個(gè)類加載器,都有一個(gè)獨(dú)立的類名稱空間。類加載器就是根據(jù)指定全限定名稱將 class 文件加載到 JVM 內(nèi)存,然后再轉(zhuǎn)化為 class 對(duì)象。
類加載器分類:
啟動(dòng)類加載器(Bootstrap ClassLoader),是虛擬機(jī)自身的一部分,用來加載Java_HOME/lib/目錄中的,或者被 -Xbootclasspath 參數(shù)所指定的路徑中并且被虛擬機(jī)識(shí)別的類庫;
擴(kuò)展類加載器(Extension ClassLoader):負(fù)責(zé)加載<java_home style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; outline: 0px !important;">\lib\ext目錄或Java. ext. dirs系統(tǒng)變量指定的路徑中的所有類庫;</java_home>
應(yīng)用程序類加載器(Application ClassLoader)。負(fù)責(zé)加載用戶類路徑(classpath)上的指定類庫,我們可以直接使用這個(gè)類加載器。一般情況,如果我們沒有自定義類加載器默認(rèn)就是用這個(gè)加載器。
自定義類加載器
他們之間如圖所示是自上向下的關(guān)系。
如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己去加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器去完成,每一層的類加載器都是如此,這樣所有的加載請(qǐng)求都會(huì)被傳送到頂層的啟動(dòng)類加載器中,只有當(dāng)父加載無法完成加載請(qǐng)求(它的搜索范圍中沒找到所需的類)時(shí),再從上向下讓子加載器嘗試去加載類。
那么我們?nèi)绾稳ヲ?yàn)證這一說法呢?
我們寫一個(gè)簡單地小程序:
然后編譯這個(gè)java文件,生成class文件。
我們把這個(gè)文件放在啟動(dòng)類加載器可以加載到的地方新建目錄classes:D:\Program Files\Java\jdk1.8.0_161\jre\classes
然后將程序的修改:
再次編譯,并將生成的classes文件放在擴(kuò)展類啟動(dòng)器可以加載到的地方新建文件夾classes:D:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\classes
最后,我們再次修改
生成的class文件就放在當(dāng)前目錄下。
那么當(dāng)我們執(zhí)行這個(gè)class文件的時(shí)候出現(xiàn)的結(jié)果是什么呢?
這說明,并沒有加載我們當(dāng)前目錄下的class文件,而是用了啟動(dòng)類加載器掃描范圍內(nèi)的那個(gè)文件。
進(jìn)一步驗(yàn)證,我們刪掉D:\Program Files\Java\jdk1.8.0_161\jre\classes下的文件
再次運(yùn)行結(jié)果:
結(jié)果變?yōu)閎bb,說明當(dāng)啟動(dòng)類加載器沒找到class文件,由擴(kuò)展類加載器加載了。
擴(kuò)展類加載器范圍內(nèi)的文件也刪掉呢?
終于加載到了當(dāng)前文件夾下的class文件
面試題:
在自己的代碼中,可以創(chuàng)建一個(gè)java.lang.String對(duì)象嗎?如果可以,這個(gè)對(duì)象是否可以被類加載器加載?
可以創(chuàng)建,但是不能被加載到。
因?yàn)椋p親委派模式會(huì)保證父類加載器先加載類,就是BootStrap(啟動(dòng)類)加載器加載jdk里面的java.lang.String類,而自定義的java.lang.String類永遠(yuǎn)不會(huì)被加載到
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Eclipse可視化插件WindowBuilder的安裝方法
這篇文章主要介紹了Eclipse可視化插件WindowBuilder的安裝方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06spring?boot集成redisson的最佳實(shí)踐示例
這篇文章主要為大家介紹了spring?boot集成redisson的最佳實(shí)踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03Java讀取網(wǎng)頁內(nèi)容并下載圖片的實(shí)例
這篇文章主要介紹了Java讀取網(wǎng)頁內(nèi)容并下載圖片的實(shí)例的相關(guān)資料,希望通過本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09Spring JPA學(xué)習(xí)之delete方法示例詳解
這篇文章主要為大家介紹了Spring JPA學(xué)習(xí)delete方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04解決idea啟動(dòng)報(bào)錯(cuò)javax.imageio.IIOException的問題
這篇文章主要介紹了idea啟動(dòng)報(bào)錯(cuò)javax.imageio.IIOException,解決打不開idea問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09