JVM系列(一)之類加載器(classloader),類加載機制、雙親委派模型

什么是classloader?

??類的加載指的是將類的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在堆區(qū)創(chuàng)建一個 java.lang.Class對象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。類的加載的最終產(chǎn)品是位于堆區(qū)中的 Class對象, Class對象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu),并且向Java程序員提供了訪問方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口。
??類加載器并不需要等到某個類被“首次主動使用”時再加載它,JVM規(guī)范允許類加載器在預(yù)料某個類將要被使用時就預(yù)先加載它,如果在預(yù)先加載的過程中遇到了.class文件缺失或存在錯誤,類加載器必須在程序首次主動使用該類時才報告錯誤(LinkageError錯誤)如果這個類一直沒有被程序主動使用,那么類加載器就不會報告錯誤。

加載.class文件的方式

1、 從本地系統(tǒng)中直接加載
2、 通過網(wǎng)絡(luò)下載.class文件
3、 從zip,jar等歸檔文件中加載.class文件
4、 從專有數(shù)據(jù)庫中提取.class文件
5、 將Java源文件動態(tài)編譯為.class文件
6、 作用:裝載.class文件

classloader 有兩種裝載class的方式 (時機):

  1. 隱式:運行過程中,碰到new方式生成對象時,隱式調(diào)用classLoader到JVM
  2. 顯式:通過class.forname()動態(tài)加載

類的生命周期

??類的生命周期包括這幾個部分,加載、連接、初始化、使用和卸載。類的加載過程又分為:加載、驗證、準(zhǔn)備、解析、初始化。

  • 加載,查找并加載類的二進(jìn)制數(shù)據(jù),在Java堆中也創(chuàng)建一個java.lang.Class類的對象
  • 連接,連接又包含三塊內(nèi)容:驗證、準(zhǔn)備、初始化。
    1)驗證,文件格式、元數(shù)據(jù)、字節(jié)碼、符號引用驗證;
    2)準(zhǔn)備,為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值;
    3)解析,把類中的符號引用轉(zhuǎn)換為直接引用
  • 初始化,為類的靜態(tài)變量賦予正確的初始值
  • 使用,new出對象程序中使用
  • 卸載,執(zhí)行垃圾回收

類加載器

分類:啟動類加載器、擴展類加載器、應(yīng)用程序類加載器。

  • 啟動類加載器(父類):Bootstrap ClassLoader,當(dāng)運行 java 虛擬機時,這個類加載器被創(chuàng)建,它負(fù)責(zé)加載虛擬機的核心類庫,如 java.lang.* 等。例如 java.lang.Object 就是由根類加載器加載的。需要注意的是,這個類加載器不是用 java 語言寫的,而是用 C/C++ 寫的。

  • 擴展類加載器:Extension ClassLoader,這個加載器加載出了基本 API 之外的一些拓展類。

  • 應(yīng)用程序類加載器:Application ClassLoader,加載應(yīng)用程序和程序員自定義的類。

??除了以上虛擬機自帶的加載器以外,用戶還可以定制自己的類加載器(User-defined Class Loader)。Java 提供了抽象類 java.lang.ClassLoader,所有用戶自定義的類加載器應(yīng)該繼承 ClassLoader 類。


類的加載機制

  • 全盤負(fù)責(zé),當(dāng)一個類加載器負(fù)責(zé)加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入,除非顯示使用另外一個類加載器來載入
  • 父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類
  • 緩存機制,緩存機制將會保證所有加載過的Class都會被緩存,當(dāng)程序中需要使用某個Class時,類加載器先從緩存區(qū)尋找該Class,只有緩存區(qū)不存在,系統(tǒng)才會讀取該類對應(yīng)的二進(jìn)制數(shù)據(jù),并將其轉(zhuǎn)換成Class對象,存入緩存區(qū)。這就是為什么修改了Class后,必須重啟JVM,程序的修改才會生效

雙親委派模型

??類的加載過程采用雙親委托機制,這種機制能更好的保證 Java 平臺的安全。
??該模型要求除了頂層的Bootstrap class loader啟動類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。子類加載器和父類加載器不是以繼承(Inheritance)的關(guān)系來實現(xiàn),而是通過組合(Composition)關(guān)系來復(fù)用父加載器的代碼。
??每個類加載器都有自己的命名空間(由該加載器及所有父類加載器所加載的類組成,在同一個命名空間中,不會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類;在不同的命名空間中,有可能會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類)

雙親委派模型的工作過程為:

??1、當(dāng)前 ClassLoader 首先從自己已經(jīng)加載的類中查詢是否此類已經(jīng)加載,如果已經(jīng)加載則直接返回原來已經(jīng)加載的類。

每個類加載器都有自己的加載緩存,當(dāng)一個類被加載了以后就會放入緩存,等下次加載的時候就可以直接返回了。

??2、當(dāng)前 classLoader 的緩存中沒有找到被加載的類的時候,委托父類加載器去加載,父類加載器采用同樣的策略,首先查看自己的緩存,然后委托父類的父類去加載,一直到 bootstrap ClassLoader.
??3、當(dāng)所有的父類加載器都沒有加載的時候,再由當(dāng)前的類加載器加載,并將其放入它自己的緩存中,以便下次有加載請求的時候直接返回。

使用這種模型來組織類加載器之間的關(guān)系的好處:

??主要是為了安全性,避免用戶自己編寫的類動態(tài)替換 Java 的一些核心類,比如 String,同時也避免了重復(fù)加載,因為 JVM 中區(qū)分不同類,不僅僅是根據(jù)類名,相同的 class 文件被不同的 ClassLoader 加載就是不同的兩個類,如果相互轉(zhuǎn)型的話會拋java.lang.ClassCaseException。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。