JVM加載class文件的原理機(jī)制實(shí)例詳解
一、JVM簡(jiǎn)介
JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫(xiě),JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。
Java語(yǔ)言的一個(gè)非常重要的特點(diǎn)就是與平臺(tái)的 無(wú)關(guān)性。而使用Java虛擬機(jī)是實(shí)現(xiàn)這一特點(diǎn)的關(guān)鍵。一般的高級(jí)語(yǔ)言如果要在不同的平臺(tái)上運(yùn)行,至少需要編譯成不同的目標(biāo)代碼。而引入Java語(yǔ)言虛擬機(jī)后,Java語(yǔ)言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯。Java語(yǔ)言使用Java虛擬機(jī)屏蔽了與平臺(tái)相關(guān)的信息,使得Java語(yǔ)言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改的運(yùn)行。Java虛擬機(jī)在執(zhí)行字節(jié)碼時(shí),把字節(jié)碼解釋成具體平臺(tái)上的及其指令執(zhí)行。這就是Java能夠“一次編譯,到處運(yùn)行”的原因。
二、JVM的組成部分
由圖可以看出,JVM是運(yùn)行在操作系統(tǒng)之上的,它與硬件沒(méi)有直接的交互。
1. 類加載器 Class Loader
類加載器的作用是加載類文件到內(nèi)存,比如編寫(xiě)一個(gè)HelloWorld.java程序,然后通過(guò)javac編譯生成class文件。由Class Loader將class文件加載到內(nèi)存中。但是Class Loader加載class文件有格式要求。
注意:Class Loader只管加載,只要符合文件結(jié)構(gòu)就加載,至于能不能運(yùn)行,是由Execution Engine負(fù)責(zé)。
2. 執(zhí)行引擎 Exexution Engine
執(zhí)行引擎也叫作解釋器,負(fù)責(zé)解釋命令,提交操作系統(tǒng)執(zhí)行。
3. 本地接口 Native Interface
本地接口的作用是為了融合不同的編程語(yǔ)言為Java所用。它的初衷是為了融合C/C++程序,Java誕生的時(shí)候是C/C++橫行的時(shí)候,要想立足,必須要有一個(gè)聰明的、睿智的調(diào)用C/C++程序,于是就在內(nèi)存中專門(mén)開(kāi)辟了一塊區(qū)域處理標(biāo)記為native的代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執(zhí)行時(shí)加載加載native libraries。目前該方法只有在與硬件有關(guān)的應(yīng)用中才會(huì)使用,在企業(yè)級(jí)應(yīng)用中已經(jīng)比較少見(jiàn),因?yàn)楝F(xiàn)在的異構(gòu)領(lǐng)域間的通信很發(fā)達(dá),比如可以使用Socket通信,也可以使用WebService等。
4. 運(yùn)行數(shù)據(jù)區(qū) Runtime data area
運(yùn)行數(shù)據(jù)區(qū)使整個(gè)JVM的重點(diǎn)。我們所寫(xiě)的程序都被加載到這里,之后才開(kāi)始運(yùn)行,Java生態(tài)系統(tǒng)如此的繁榮,得益于該區(qū)域的優(yōu)良自治。
三、JVM加載class文件的原理機(jī)制
1. Java中的所有類,必須被裝載到JVM中才能運(yùn)行,這個(gè)裝載工作是由JVM中的類裝載器完成的,類裝載器所做的工作實(shí)質(zhì)是把類文件從硬盤(pán)讀取到內(nèi)存中,作用就是在運(yùn)行時(shí)加載類。
Java類加載器基于三個(gè)機(jī)制:委托、可見(jiàn)性和單一性。
(1)委托機(jī)制是指加載一個(gè)類的請(qǐng)求交給父類加載器,如果這個(gè)父類加載器不能夠找到或加載這個(gè)類,那么再加載它。
(2)可見(jiàn)性的原理是子類的加載器可以看見(jiàn)所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。
(3)單一性原理是指一個(gè)類僅被加載一次,這是由委托機(jī)制確保子類加載器不會(huì)再次加載父類加載器加載過(guò)的類。
2. Java中的類大致分為三種:
(1)系統(tǒng)類
(2)擴(kuò)展類
(3)由程序員自定義的類
3. 類裝載有兩種方式
(1)隱式裝載:
程序在運(yùn)行過(guò)程中當(dāng)碰到通過(guò)new等方式生成類或者子類對(duì)象、使用類或者子類的靜態(tài)域時(shí),隱式調(diào)用類加載器加載對(duì)應(yīng)的的類到JVM中。
(2)顯式裝載:
通過(guò)調(diào)用Class.forName()或者ClassLoader.loadClass(className)等方法,顯式加載需要的類。
4. 類加載的動(dòng)態(tài)性體現(xiàn)
一個(gè)應(yīng)用程序總是由n多個(gè)類組成,Java程序啟動(dòng)時(shí),并不是一次把所有的類全部加載再運(yùn)行,他總是把保證程序運(yùn)行的基礎(chǔ)類一次性加載到JVM中,其他類等到JVM用到的時(shí)候再加載,這樣是為了節(jié)省內(nèi)存的開(kāi)銷(xiāo),因?yàn)镴ava最早就是為嵌入式系統(tǒng)而設(shè)計(jì)的,內(nèi)存寶貴,而用到時(shí)再加載這也是Java動(dòng)態(tài)性的一種體現(xiàn)。
5. Java類加載器
Java中的類加載器實(shí)質(zhì)上也是也是類,功能是把類加載入JVM中,值得注意的是JVM的類加載器有三個(gè),原因有:一方面是為了分工明確,各自負(fù)責(zé)各自的區(qū)塊,另一方面為了實(shí)現(xiàn)委托模型。
層次結(jié)構(gòu)如下:
BootStrap Loader(引導(dǎo)類加載器) ----- 負(fù)責(zé)加載系統(tǒng)類
ExtClassLoader(擴(kuò)展類加載器) ----- 負(fù)責(zé)加載擴(kuò)展類
AppClassLoade(應(yīng)用類加載器)r ----- 負(fù)責(zé)加載應(yīng)用類
6. 類加載器之間如何協(xié)調(diào)工作的
Java中有三個(gè)類加載器,碰到一個(gè)類需要加載時(shí),Java采用委托模型機(jī)制來(lái)協(xié)調(diào)和區(qū)分該由哪個(gè)類加載器完成。簡(jiǎn)單來(lái)說(shuō)就是,“類裝載器有載入類的需求時(shí),會(huì)先請(qǐng)示其Parent使用其搜索路徑幫忙載入”,如果Parent找不到,那么才由自己依照自己的搜索路徑搜索類。
實(shí)例一:
package ClassLoaderTest; public class ClassLoaderTest { public static void main(String[] args) { ClassLoader c1 = ClassLoaderTest.class.getClassLoader(); System.out.println(c1); ClassLoader c1Parent = c1.getParent(); System.out.println(c1Parent); ClassLoader c1Root = c1Parent.getParent(); System.out.println(c1Root); } }
執(zhí)行結(jié)果:
可以看出ClassLoaderTest是由AppClassLoader加載器加載的。AppClassLoader的Parent加載器是ExtClassLoader。但是ExtClassLoader的Parent是null,在Java中是無(wú)法獲取的。
實(shí)例二:
public class Test2 { public void test(){ System.out.println(Test2.class); System.out.println(this.getClass()); System.out.println(Test2.class.getClassLoader()); } } public class Test1 { public static void main(String[] args) { System.out.println(Test1.class.getClassLoader()); Test2 test2 = new Test2(); test2.test(); } }
執(zhí)行結(jié)果:
7. 預(yù)先加載和依需求加載
Java運(yùn)行環(huán)境為了優(yōu)化系統(tǒng),提高程序的執(zhí)行速度,在JRE運(yùn)行的開(kāi)始會(huì)將Java運(yùn)行所需要的基本類采用預(yù)先加載(pre-loading)的方法全部加載到內(nèi)存當(dāng)中,因?yàn)檫@些單元在Java程序運(yùn)行的過(guò)程當(dāng)中要經(jīng)常使用的,主要包括JRE的rt.jar文件里面所有的.class文件。
當(dāng)java.exe虛擬機(jī)開(kāi)始運(yùn)行以后,它會(huì)找到安裝在機(jī)器上的JRE環(huán)境,然后把控制權(quán)交給JRE,JRE的類加載器會(huì)自動(dòng)將lib目錄下的rt.jar基礎(chǔ)類別文件庫(kù)加載進(jìn)內(nèi)存,這些文件是Java程序執(zhí)行所必需的,所以系統(tǒng)在開(kāi)始就將這些文件加載,避免以后的多次IO操作,從而提高程序執(zhí)行效率。然而我們?cè)诔绦蛑行枰褂米远x的類的時(shí)候就要使用依需求加載(load-on-demand)的方式,就是在Java程序需要用到的時(shí)候再加載,以減少內(nèi)存的消耗。
8. ClassLoader中一些 重要的方法
9.什么地方適用類加載器
最經(jīng)典的例子就是AppletClassLoader,他被用來(lái)加載Applet使用的類,而Applet大部分是在網(wǎng)上使用,而非本地的操作系統(tǒng)使用。使用不同的類加載器,你可以從不同的源地址加載同一個(gè)類,它們被視為不同的類。J2EE使用多個(gè)類加載器加載不同地方的類,例如War文件由Web-app類加載器加載,而EJB-JAR中的類由另外的類加載器加載。有些服務(wù)器也支持熱部署,這是由類加載器實(shí)現(xiàn)。你也可以使用類加載器來(lái)加載數(shù)據(jù)庫(kù)或者其他持久層的數(shù)據(jù)。
10. 類加載器的階層體系
Java類加載器的工作原理:
當(dāng)執(zhí)行Java的.class文件的時(shí)候,java.exe會(huì)幫助我們找到j(luò)RE,接著找到JRE內(nèi)部的jcm.dll,這才是真正的Java虛擬機(jī)器,最后加載動(dòng)態(tài)庫(kù),激活Java虛擬機(jī)器。虛擬機(jī)激活以后,會(huì)先做一些初始化的動(dòng)作,比如說(shuō)讀取系統(tǒng)參數(shù)等。一旦初始化動(dòng)作完成后,就會(huì)產(chǎn)生第一個(gè)類加載器-----Bootstrap Loader,Bootstrap Loader是由C++撰寫(xiě)而成,這個(gè)Bootstrap所做的初始工作中,除了一些基本的初始化動(dòng)作之外,最重要的就是加載Launcher.java之中的ExtClassLoader,并設(shè)定其parent為null,代表其父加載器為BootstrapLoader。然后Bootstrap loader再要求加載Launcher.java之中的AppClassLoader,并設(shè)定其parent為之前產(chǎn)生的ExtClassLoader實(shí)體。這兩個(gè)類加載器都是以靜態(tài)類的形式存在的。注意:Launcher E x t C l a s s L o a d e r . c l a s s 與 L a u n c h e r ExtClassLoader.class與Launcher ExtClassLoader.class與LauncherAppClassLoader.class都是由Bootstrap Loader所加載,所以Parent和由哪個(gè)類加載器加載沒(méi)有關(guān)系。
三者之間的關(guān)系:
Bootstrap Loader <—(extends)-----ExtClassLoader <—(extends)—AppClassLoader
這三個(gè)類加載器構(gòu)成了Java的類加載體系。它們分別從以下的路徑尋找程序所需要的類:
Bootstrap Loader:sun.boot.class.path
ExtClassLoader:java.ext.dirs
AppClassLoader:java.class.path
這三個(gè)參數(shù)可以通過(guò)System.getProperty()函數(shù)得到具體對(duì)應(yīng)的路徑。
到此這篇關(guān)于JVM加載class文件的原理機(jī)制的文章就介紹到這了,更多相關(guān)JVM加載class文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java并發(fā)編程專題(二)----如何創(chuàng)建并運(yùn)行java線程
這篇文章主要介紹了java并發(fā)編程如何創(chuàng)建并運(yùn)行java線程,文中講解非常詳細(xì),示例代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06Springboot使用redis進(jìn)行api防刷限流過(guò)程詳解
這篇文章主要介紹了Springboot使用redis進(jìn)行api防刷限流過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12java實(shí)現(xiàn)oracle插入當(dāng)前時(shí)間的方法
這篇文章主要介紹了java實(shí)現(xiàn)oracle插入當(dāng)前時(shí)間的方法,以實(shí)例形式對(duì)比分析了java使用Oracle操作時(shí)間的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03J2EE項(xiàng)目代碼編寫(xiě)規(guī)范分享
這篇文章主要介紹了J2EE項(xiàng)目代碼編寫(xiě)規(guī)范分享,需要的朋友可以參考下2014-10-10HashMap紅黑樹(shù)入門(mén)(實(shí)現(xiàn)一個(gè)簡(jiǎn)單的紅黑樹(shù))
紅黑樹(shù)(Red Black Tree) 是一種自平衡二叉查找樹(shù),是在計(jì)算機(jī)科學(xué)中用到的一種數(shù)據(jù)結(jié)構(gòu),典型的用途是實(shí)現(xiàn)關(guān)聯(lián)數(shù)組。 紅黑樹(shù)發(fā)明時(shí)被稱為平衡二叉B樹(shù),后來(lái)修改為如今的“紅黑樹(shù)”2021-06-06Java利用EasyExcel讀取寫(xiě)入Excel詳情
這篇文章主要介紹了Java利用EasyExcel讀取寫(xiě)入Excel詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09