JVM加載class文件的原理機制實例詳解
一、JVM簡介
JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用于計算設備的規(guī)范,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的。
Java語言的一個非常重要的特點就是與平臺的 無關性。而使用Java虛擬機是實現(xiàn)這一特點的關鍵。一般的高級語言如果要在不同的平臺上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機后,Java語言在不同平臺上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改的運行。Java虛擬機在執(zhí)行字節(jié)碼時,把字節(jié)碼解釋成具體平臺上的及其指令執(zhí)行。這就是Java能夠“一次編譯,到處運行”的原因。
二、JVM的組成部分

由圖可以看出,JVM是運行在操作系統(tǒng)之上的,它與硬件沒有直接的交互。

1. 類加載器 Class Loader
類加載器的作用是加載類文件到內存,比如編寫一個HelloWorld.java程序,然后通過javac編譯生成class文件。由Class Loader將class文件加載到內存中。但是Class Loader加載class文件有格式要求。
注意:Class Loader只管加載,只要符合文件結構就加載,至于能不能運行,是由Execution Engine負責。
2. 執(zhí)行引擎 Exexution Engine
執(zhí)行引擎也叫作解釋器,負責解釋命令,提交操作系統(tǒng)執(zhí)行。
3. 本地接口 Native Interface
本地接口的作用是為了融合不同的編程語言為Java所用。它的初衷是為了融合C/C++程序,Java誕生的時候是C/C++橫行的時候,要想立足,必須要有一個聰明的、睿智的調用C/C++程序,于是就在內存中專門開辟了一塊區(qū)域處理標記為native的代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執(zhí)行時加載加載native libraries。目前該方法只有在與硬件有關的應用中才會使用,在企業(yè)級應用中已經(jīng)比較少見,因為現(xiàn)在的異構領域間的通信很發(fā)達,比如可以使用Socket通信,也可以使用WebService等。
4. 運行數(shù)據(jù)區(qū) Runtime data area
運行數(shù)據(jù)區(qū)使整個JVM的重點。我們所寫的程序都被加載到這里,之后才開始運行,Java生態(tài)系統(tǒng)如此的繁榮,得益于該區(qū)域的優(yōu)良自治。
三、JVM加載class文件的原理機制
1. Java中的所有類,必須被裝載到JVM中才能運行,這個裝載工作是由JVM中的類裝載器完成的,類裝載器所做的工作實質是把類文件從硬盤讀取到內存中,作用就是在運行時加載類。
Java類加載器基于三個機制:委托、可見性和單一性。
(1)委托機制是指加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或加載這個類,那么再加載它。
(2)可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。
(3)單一性原理是指一個類僅被加載一次,這是由委托機制確保子類加載器不會再次加載父類加載器加載過的類。
2. Java中的類大致分為三種:
(1)系統(tǒng)類
(2)擴展類
(3)由程序員自定義的類
3. 類裝載有兩種方式
(1)隱式裝載:
程序在運行過程中當碰到通過new等方式生成類或者子類對象、使用類或者子類的靜態(tài)域時,隱式調用類加載器加載對應的的類到JVM中。
(2)顯式裝載:
通過調用Class.forName()或者ClassLoader.loadClass(className)等方法,顯式加載需要的類。
4. 類加載的動態(tài)性體現(xiàn)
一個應用程序總是由n多個類組成,Java程序啟動時,并不是一次把所有的類全部加載再運行,他總是把保證程序運行的基礎類一次性加載到JVM中,其他類等到JVM用到的時候再加載,這樣是為了節(jié)省內存的開銷,因為Java最早就是為嵌入式系統(tǒng)而設計的,內存寶貴,而用到時再加載這也是Java動態(tài)性的一種體現(xiàn)。
5. Java類加載器
Java中的類加載器實質上也是也是類,功能是把類加載入JVM中,值得注意的是JVM的類加載器有三個,原因有:一方面是為了分工明確,各自負責各自的區(qū)塊,另一方面為了實現(xiàn)委托模型。
層次結構如下:
BootStrap Loader(引導類加載器) ----- 負責加載系統(tǒng)類
ExtClassLoader(擴展類加載器) ----- 負責加載擴展類
AppClassLoade(應用類加載器)r ----- 負責加載應用類
6. 類加載器之間如何協(xié)調工作的
Java中有三個類加載器,碰到一個類需要加載時,Java采用委托模型機制來協(xié)調和區(qū)分該由哪個類加載器完成。簡單來說就是,“類裝載器有載入類的需求時,會先請示其Parent使用其搜索路徑幫忙載入”,如果Parent找不到,那么才由自己依照自己的搜索路徑搜索類。
實例一:
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í)行結果:

可以看出ClassLoaderTest是由AppClassLoader加載器加載的。AppClassLoader的Parent加載器是ExtClassLoader。但是ExtClassLoader的Parent是null,在Java中是無法獲取的。
實例二:
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í)行結果:

7. 預先加載和依需求加載
Java運行環(huán)境為了優(yōu)化系統(tǒng),提高程序的執(zhí)行速度,在JRE運行的開始會將Java運行所需要的基本類采用預先加載(pre-loading)的方法全部加載到內存當中,因為這些單元在Java程序運行的過程當中要經(jīng)常使用的,主要包括JRE的rt.jar文件里面所有的.class文件。
當java.exe虛擬機開始運行以后,它會找到安裝在機器上的JRE環(huán)境,然后把控制權交給JRE,JRE的類加載器會自動將lib目錄下的rt.jar基礎類別文件庫加載進內存,這些文件是Java程序執(zhí)行所必需的,所以系統(tǒng)在開始就將這些文件加載,避免以后的多次IO操作,從而提高程序執(zhí)行效率。然而我們在程序中需要使用自定義的類的時候就要使用依需求加載(load-on-demand)的方式,就是在Java程序需要用到的時候再加載,以減少內存的消耗。
8. ClassLoader中一些 重要的方法

9.什么地方適用類加載器
最經(jīng)典的例子就是AppletClassLoader,他被用來加載Applet使用的類,而Applet大部分是在網(wǎng)上使用,而非本地的操作系統(tǒng)使用。使用不同的類加載器,你可以從不同的源地址加載同一個類,它們被視為不同的類。J2EE使用多個類加載器加載不同地方的類,例如War文件由Web-app類加載器加載,而EJB-JAR中的類由另外的類加載器加載。有些服務器也支持熱部署,這是由類加載器實現(xiàn)。你也可以使用類加載器來加載數(shù)據(jù)庫或者其他持久層的數(shù)據(jù)。
10. 類加載器的階層體系
Java類加載器的工作原理:
當執(zhí)行Java的.class文件的時候,java.exe會幫助我們找到jRE,接著找到JRE內部的jcm.dll,這才是真正的Java虛擬機器,最后加載動態(tài)庫,激活Java虛擬機器。虛擬機激活以后,會先做一些初始化的動作,比如說讀取系統(tǒng)參數(shù)等。一旦初始化動作完成后,就會產生第一個類加載器-----Bootstrap Loader,Bootstrap Loader是由C++撰寫而成,這個Bootstrap所做的初始工作中,除了一些基本的初始化動作之外,最重要的就是加載Launcher.java之中的ExtClassLoader,并設定其parent為null,代表其父加載器為BootstrapLoader。然后Bootstrap loader再要求加載Launcher.java之中的AppClassLoader,并設定其parent為之前產生的ExtClassLoader實體。這兩個類加載器都是以靜態(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和由哪個類加載器加載沒有關系。
三者之間的關系:
Bootstrap Loader <—(extends)-----ExtClassLoader <—(extends)—AppClassLoader
這三個類加載器構成了Java的類加載體系。它們分別從以下的路徑尋找程序所需要的類:
Bootstrap Loader:sun.boot.class.path
ExtClassLoader:java.ext.dirs
AppClassLoader:java.class.path
這三個參數(shù)可以通過System.getProperty()函數(shù)得到具體對應的路徑。
到此這篇關于JVM加載class文件的原理機制的文章就介紹到這了,更多相關JVM加載class文件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java日期格式化的實現(xiàn)(@JsonFormat和@JSONField)
本文主要介紹了Java日期格式化的實現(xiàn),主要介紹了@JsonFormat和@JSONField兩種方式,具有一定的參考價值,感興趣的可以了解一下2024-05-05
Java數(shù)據(jù)庫連接池的幾種配置方法(以MySQL數(shù)據(jù)庫為例)
這篇文章主要介紹了Java數(shù)據(jù)庫連接池的幾種配置方法(以MySQL數(shù)據(jù)庫為例) 的相關資料,需要的朋友可以參考下2016-07-07
Mybatis-Plus中分頁插件PaginationInterceptor的使用
我們在開發(fā)的過程中,經(jīng)常會遇到分頁操作,本文主要介紹了Mybatis-Plus中分頁插件PaginationInterceptor的使用,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06
SpringBoot項目使用validated實現(xiàn)參數(shù)校驗框架
當談到Spring的參數(shù)校驗功能時,@Validated注解無疑是一個重要的利器,它為我們提供了一種簡單而又強大的方式來驗證請求參數(shù)的合法性,保證了系統(tǒng)的穩(wěn)定性和安全性,本文將介紹Spring Validated的基本用法以及在實際項目中的應用,需要的朋友可以參考下2024-05-05

