成都校區*精品*類加載器解析

為什麼要使用類加載器

類加載器作用於類加載過程中加載環節,是將.class文件加載進入內存時所使用到的技術,下文介紹類加載器

類加載的機制

加載: 1.取得類的二進制流, 2.轉為方法區數據結構, 3.在Java堆中生成對應java.lang.Class對象

鏈接

1.驗證:保證Class流的格式是正確的

一、文件格式的驗證(是否以0xCAFEBABE開頭)。

二、元數據驗證(是否有父類,繼承了final類?非抽象類實現了所有的抽象方法。

三、字節碼驗證 (很複雜)。四、符號引用驗證)

2.準備:分配內存,併為類的靜態變量設置默認初始值 (方法區中),注意:此時對象並未創建,且常量在該階段就完成賦值

3.解析:符號引用替換為直接引用

1)、符號引用:Java中,一個java類將會編譯成一個class文件。在編譯時,java類並不知道所引用的類的實際地址,因此只能使用符號引用來代替。比如org.simple.People類引用了org.simple.Language類,在編譯時People類並不知道Language類的實際內存地址

2)、直接引用:、實際內存地址,指向了實際的內存如果有了直接引用,那引用的目標必定已經被加載入內存中了。

初始化:執行類構造器 1、static變量 賦值語句 2、static{}語句 3、子類的調用前保證父類的被調用 4、是線程安全的

判斷什麼時候需要進行初始化

有6種情況稱之為主動使用,在主動使用的情況下,該類會被類加載器加入內存,且進行初始化,除此以外都不會加載該類 創建類的實例

– 訪問某個類或接口的靜態變量,或者對該靜態 變量賦值

– 調用類的靜態方法

– 反射(如Class.forName(“java.lang.String”)

– 初始化一個類的子類

– Java虛擬機啟動時被標明為啟動類的類(Java Test)

類加載器的介紹

BootStrap ClassLoader (啟動ClassLoader,根類加載器) rt.jar java.lang Extension ClassLoader (擴展ClassLoader) ext包下的 內容 App ClassLoader (應用ClassLoader/系統ClassLoader) 開發人員自己寫的 Custom ClassLoader(自定義ClassLoader) 根類加載器並不是由Java代碼寫的,且sun公司也不提供對根類加載器的任何操作,想要拿到這個一個加載器,返回的結果是null除了根類加載器以外,其他的加載器都是java代碼寫的,上一層是下一層的父容器 ,值得注意的是他們並不是一個繼承關係,而是邏輯上的父子關係 啟動類加載器,由C++實現,沒有父類。 拓展類加載器(ExtClassLoader),由Java語言實現,父類加載器為BootStrap 系統類加載器(AppClassLoader),由Java語言實現,父類加載器為ExtClassLoader 自定義類加載器,父類加載器肯定為AppClassLoader。

雙親委派模式工作原理

原理:如果一個類加載器收到了類加載請求,它並不會自己先去加載,而是把這個請求委託給父類的加載器去執行,如果父類加載器還存在其父類加載器,則進一步向上委託,依次遞歸,請求最終將到達頂層的啟動類加載器,如果父類加載器可以完成類加載任務,就成功返回,倘若父類加載器無法完成此加載任務,子加載器才會嘗試自己去加載,這就是雙親委派模式,

雙親機制的目的:防止對象的重複加載! 舉例:每個類加載器都能加載對應不同文件的類,那麼假如我們自己定義了一個java.lang.Object包下的內容,那麼首先這個類是我們自己寫的,也存在我們的classpath路徑下,會被app加載器加載,但同樣,JDK中的Object也會被加載,產生多個對象,造成混亂!

接下來我們再來類加載器代碼中怎麼保證父類雙親委託機制 該方法加載指定名稱(包括包名)的二進制類型,該方法在JDK1.2之後不再建議用戶重寫但用戶可以直接調用該方法,loadClass()方法是ClassLoader類自己實現的,該方法中的邏輯就是雙親委派模式的實現

[Java]

protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 先從緩存查找該class對象,找到就不用重新加載
Class> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//如果找不到,則委託給父類加載器去加載
if (parent != null) {

c = parent.loadClass(name, false);
} else {
//如果沒有父類,則委託給啟動加載器去加載
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}

if (c == null) {
long t1 = System.nanoTime();
// 如果都沒有找到,則通過自定義實現的findClass去查找並加載
c = findClass(name);

sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}


而對應findClass是在CLassloader接口中用於給子類重寫使用的源碼中只是一個空實現

[Java]

protected Class> findClass(String name) throws ClassNotFoundException {

throw new ClassNotFoundException(name);

}

雙親委派模型的破壞者

介紹:打破雙親委派機制有3種方式,雙親委派的核心是由下向上委託,而所謂的打破,指的是在加載資源時需要從上往下尋找

1.類加載器是Jdk1.0以前出現的,而雙親委派原則是Jdk1.2出現的,為了兼容以前的程序 2.熱部署,熱部署會使得每個點都具有一個類加載器,從而使得類加載器形成一個網狀結構,從而打破了雙親委派機制 3.Java提供了很多服務提供者接口(Service Provider Interface,SPI)這些接口允許第三方為它們提供實現,如常見的 SPI 有 JDBC、JNDI等,這些 SPI 的接口屬於 Java 核心庫,一般存在rt.jar包中,由Bootstrap類加載器加載,而 SPI 的第三方實現代碼則是作為Java應用所依賴的 jar 包被存放在classpath路徑下,由於SPI接口中的代碼經常需要加載具體的第三方實現類並調用其相關方法,但SPI的核心接口類是由引導類加載器來加載的,而Bootstrap類加載器無法直接加載SPI的實現類,同時由於雙親委派模式的存在,Bootstrap類加載器也無法反向委託AppClassLoader加載器SPI的實現類。在這種情況下,我們就需要一種特殊的類加載器來加載第三方的類庫,使用線程類上下文類加載器其核心原理就是: 線程類上下文類加載器是調用java.lang.Thread 包下的getContextClassLoader 和setContextClassLoader-->如果沒有設置的話默認就是appClassLoader



分享到:


相關文章: