您好,登錄后才能下訂單哦!
本篇內容主要講解“JVM的類加載過程以及雙親委派模型詳細介紹”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“JVM的類加載過程以及雙親委派模型詳細介紹”吧!
jvm 的主要組成部分
類加載器(ClassLoader)
運行時數據區(Runtime Data Area)
執行引擎(Execution Engine)
本地庫接口(Native Interface)
jvm 運行時數據區的組成
方法區:
①方法區主要用來存儲已被虛擬機加載的類信息(構造器,接口定義)、常量、靜態變量和運行時常量池等數據。
②該區域是被線程共享的。
③方法區里有一個運行時常量池,用于存放靜態編譯產生的字面量和符號引用。該常量池具有動態性,也就是說常量并不一定是編譯時確定,運行時生成的常量也會存在這個常量池中。
虛擬機棧:
虛擬機棧也叫棧內存,主管Java程序的運行,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對于棧來說不存在垃圾回收問題,只要線程一結束該棧就Over,生命周期和線程一致,是線程私有的。
8種基本類型的變量+對象的引用變量+實例方法都是在函數的棧內存中分配。
①每個方法在執行的時候都會創建一個棧幀,用于存儲局部變量表、操作數棧、動態鏈接和方法出口等信息。
②虛擬機棧是線程私有的,它的生命周期與線程相同。
③局部變量表里存儲的是基本數據類型、returnAddress類型(指向一條字節碼指令的地址)和對象引用,這個對象引用有可能是指向對象起始地址的一個指針,也有可能是代表對象的句柄或者與對象相關聯的位置。4.局部變量所需的內存空間在編譯器間確定。
④操作數棧的作用主要用來存儲運算結果以及運算的操作數,它不同于局部變量表通過索引來訪問,而是壓棧和出棧的方式
⑤每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調用過程中的動態連接.動態鏈接就是將常量池中的符號引用在運行期轉化為直接引用。
本地方法棧
本地方法棧和虛擬機棧類似,只不過本地方法棧為Native方法服務。
堆
java堆是所有線程所共享的一塊內存,在虛擬機啟動時創建,幾乎所有的對象實例都在這里創建,因此該區域經常發生垃圾回收操作。
程序計數器
內存空間小,字節碼解釋器工作時通過改變這個計數值可以選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理和線程恢復等功能都需要依賴這個計數器完成。該內存區域是唯一一個java虛擬機規范沒有規定任何OOM(程序申請內存過大,虛擬機無法滿足我們,然后自殺了)情況的區域。
程序在虛擬機中的執行過程
首先是類加載器來加載class文件得到Class模板,放到方法區中(類的信息(構造器,接口定義)、常量、靜態變量和運行時常量池等數據),根據class模板來實例化對象的時候,會把對象放在堆中(可以提一下堆分代,垃圾回收策略,垃圾回收算法,內存泄漏原因),根據對象調用方法時,會將方法壓到棧中(8種基本類型的變量+對象的引用變量+實例方法),native方法會被壓入到本地方法棧中,由jvm向操作系統發送指令,由執行引擎解釋命令發送給操作系統,操作系統會調用本地方法接口,用本地方法庫,執行本地方法。棧中的方法按照后進先出的順序出棧,由程序計數器來指向下一個出棧的方法,棧中沒有垃圾回收,他們隨著線程的執行完畢被釋放。
類加載的雙親委派模型
在介紹雙親委派模型之前先說下類加載器。對于任意一個類,都需要由加載它的類加載器和這個類本身一同確立在 JVM 中的唯一性,每一個類加載器,都有一個獨立的類名稱空間。類加載器就是根據指定全限定名稱將 class 文件加載到 JVM 內存,然后再轉化為 class 對象。
類加載器分類:
啟動類加載器(Bootstrap ClassLoader),是虛擬機自身的一部分,用來加載Java_HOME/lib/目錄中的,或者被 -Xbootclasspath 參數所指定的路徑中并且被虛擬機識別的類庫;
擴展類加載器(Extension ClassLoader):負責加載<java_home >\lib\ext目錄或Java. ext. dirs系統變量指定的路徑中的所有類庫;</java_home>
應用程序類加載器(Application ClassLoader)。負責加載用戶類路徑(classpath)上的指定類庫,我們可以直接使用這個類加載器。一般情況,如果我們沒有自定義類加載器默認就是用這個加載器。
自定義類加載器
他們之間如圖所示是自上向下的關系。
如果一個類加載器收到了類加載的請求,它首先不會自己去加載這個類,而是把這個請求委派給父類加載器去完成,每一層的類加載器都是如此,這樣所有的加載請求都會被傳送到頂層的啟動類加載器中,只有當父加載無法完成加載請求(它的搜索范圍中沒找到所需的類)時,再從上向下讓子加載器嘗試去加載類。
那么我們如何去驗證這一說法呢?
我們寫一個簡單地小程序:
然后編譯這個java文件,生成class文件。
我們把這個文件放在啟動類加載器可以加載到的地方新建目錄classes:D:\Program Files\Java\jdk1.8.0_161\jre\classes
然后將程序的修改:
再次編譯,并將生成的classes文件放在擴展類啟動器可以加載到的地方新建文件夾classes:D:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\classes
最后,我們再次修改
生成的class文件就放在當前目錄下。
那么當我們執行這個class文件的時候出現的結果是什么呢?
這說明,并沒有加載我們當前目錄下的class文件,而是用了啟動類加載器掃描范圍內的那個文件。
進一步驗證,我們刪掉D:\Program Files\Java\jdk1.8.0_161\jre\classes下的文件
再次運行結果:
結果變為bbb,說明當啟動類加載器沒找到class文件,由擴展類加載器加載了。
擴展類加載器范圍內的文件也刪掉呢?
終于加載到了當前文件夾下的class文件
到此,相信大家對“JVM的類加載過程以及雙親委派模型詳細介紹”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。