Java類載入原理解析
每個java開發人員對java.lang.ClassNotFoundExcetpion這個異常肯定都不陌生,這背後就涉及到了java技術體系中的類載入。Java的類載入機制是java技術體系中比較核心的部分,雖然和大部分開發人員直接打交道不多,但是對其背後的機理有一定理解有助於排查程式中出現的類載入失敗等技術問題,對理解java虛擬機器的連線模型和java語言的動態性都有很大幫助。
由於關於java類載入的內容較多,所以打算分三篇文章簡述一下:
第一篇:java類載入原理解析
第二篇:外掛環境下類載入原理解析
第三篇:執行緒上下文類載入器
類:開發技術->J2EE
標籤:Java類載入 類載入器 雙親委派機制 自定義類載入器
作者:朱興 創建於2007-6-22 MSN:[email protected]
2 Java虛擬機器類載入器結構簡述
2.1 JVM三種預定義型別類載入器
我們首先看一下JVM預定義的三種類型類載入器,當一個 JVM 啟動的時候,Java 預設開始使用如下三種類型類裝入器:
啟動(Bootstrap)類載入器:引導類裝入器是用原生代碼實現的類裝入器,它負責將 <Java_Runtime_Home>/lib 下面的類庫載入到記憶體中。由於引導類載入器涉及到虛擬機器本地實現細節,開發者無法直接獲取到啟動類載入器的引用,所以不允許直接通過引用進行操作。
標準擴充套件(Extension)類載入器:擴充套件類載入器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 實現的。它負責將 < Java_Runtime_Home >/lib/ext 或者由系統變數 java.ext.dir 指定位置中的類庫載入到記憶體中。開發者可以直接使用標準擴充套件類載入器。
系統(System)類載入器:系統類載入器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的。它負責將系統類路徑(CLASSPATH)中指定的類庫載入到記憶體中。開發者可以直接使用系統類載入器。
除了以上列舉的三種類載入器,還有一種比較特殊的型別就是執行緒上下文類載入器,這個將在後面單獨介紹。
2.2 類載入雙親委派機制介紹和分析
在這裡,需要著重說明的是,JVM在載入類時預設採用的是雙親委派機制。通俗的講,就是某個特定的類載入器在接到載入類的請求時,首先將載入任務委託給父類載入器,依次遞迴,如果父類載入器可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入任務時,才自己去載入。關於虛擬機器預設的雙親委派機制,我們可以從系統類載入器和標準擴充套件類載入器為例作簡單分析。
圖一 標準擴充套件類載入器繼承層次圖
圖二 系統類載入器繼承層次圖
通過圖一和圖二我們可以看出,類載入器均是繼承自java.lang.ClassLoader抽象類。我們下面我們就看簡要介紹一下java.lang.ClassLoader中幾個最重要的方法:
//載入指定名稱(包括包名)的二進位制型別,供使用者呼叫的介面
public Class<?> loadClass(String name) throws ClassNotFoundException{//…}
//載入指定名稱(包括包名)的二進位制型別,同時指定是否解析(但是,這裡的resolve引數不一定真正能達到解析的效果~_~),供繼承用
protectedsynchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{//…}
//findClass方法一般被loadClass方法呼叫去載入指定名稱類,供繼承用
protected Class<?> findClass(String name) throws ClassNotFoundException {//…}
//定義型別,一般在findClass方法中讀取到對應位元組碼後呼叫,可以看出不可繼承(說明:JVM已經實現了對應的具體功能,解析對應的位元組碼,產生對應的內部資料結構放置到方法區,所以無需覆寫,直接呼叫就可以了)
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError{//…}
通過進一步分析標準擴充套件類載入器(sun.misc.Launcher$ExtClassLoader)和系統類載入器(sun.misc.Launcher$AppClassLoader)的程式碼以及其公共父類(java.net.URLClassLoader和java.security.SecureClassLoader)的程式碼可以看出,都沒有覆寫java.lang.ClassLoader中預設的載入委派規則---loadClass(…)方法。既然這樣,我們就可以通過分析java.lang.ClassLoader中的loadClass(String name)方法的程式碼就可以分析出虛擬機器預設採用的雙親委派機制到底是什麼模樣:
public Class<?> loadClass(String name)throws ClassNotFoundException {
return loadClass(name, false);
}
protectedsynchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 首先判斷該型別是否已經被載入
Class c = findLoadedClass(name);
if (c == null) {
//如果沒有被載入,就委託給父類載入或者委派給啟動類載入器載入
try {
if (parent != null) {
//如果存在父類載入器,就委派給父類載入器載入
c = parent.loadClass(name, false);
} else {
//如果不存在父類載入器,就檢查是否是由啟動類載入器載入的類,通過呼叫本地方法native Class findBootstrapClass(String name)
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// 如果父類載入器和啟動類載入器都不能完成載入任務,才呼叫自身的載入功能
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
通過上面的程式碼分析,我們可以對JVM採用的雙親委派類載入機制有了更感性的認識,下面我們就接著分析一下啟動類載入器、標準擴充套件類載入器和系統類載入器三者之間的關係。可能大家已經從各種資料上面看到了如下類似的一幅圖片:
圖三 類載入器預設委派關係圖
上面圖片給人的直觀印象是系統類載入器的父類載入器是標準擴充套件類載入器,標準擴充套件類載入器的父類載入器是啟動類載入器,下面我們就用程式碼具體測試一下:
示例程式碼:
try {
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent();
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
} catch (Exception e) {
e.printStackTrace();
}
}
說明:通過java.lang.ClassLoader.getSystemClassLoader()可以直接獲取到系統類載入器。
程式碼輸出如下:
null
通過以上的程式碼輸出,我們可以判定系統類載入器的父載入器是標準擴充套件類載入器,但是我們試圖獲取標準擴充套件類載入器的父類載入器時確得到了null,就是說標準擴充套件類載入器本身強制設定父類載入器為null。我們還是藉助於程式碼分析一下:
我們首先看一下java.lang.ClassLoader抽象類中預設實現的兩個建構函式:
protected ClassLoader() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
//預設將父類載入器設定為系統類載入器,getSystemClassLoader()獲取系統類載入器
this.parent = getSystemClassLoader();
initialized = true;
}
protected ClassLoader(ClassLoader parent) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
//強制設定父類載入器
this.parent = parent;
initialized = true;
}
我們再看一下ClassLoader抽象類中parent成員的宣告:
// The parent class loader for delegation
private ClassLoader parent;
宣告為私有變數的同時並沒有對外提供可供派生類訪問的public或者protected設定器介面(對應的setter方法),結合前面的測試程式碼的輸出,我們可以推斷出:
1. 系統類載入器(AppClassLoader)呼叫ClassLoader(ClassLoader parent)建構函式將父類載入器設定為標準擴充套件類載入器(ExtClassLoader)。(因為如果不強制設定,預設會通過呼叫getSystemClassLoader()方法獲取並設定成系統類載入器,這顯然和測試輸出結果不符。)
2. 擴充套件類載入器(ExtClassLoader)呼叫ClassLoader(ClassLoader parent)建構函式將父類載入器設定為null。(因為如果不強制設定,預設會通過呼叫getSystemClassLoader()方法獲取並設定成系統類載入器,這顯然和測試輸出結果不符。)
現在我們可能會有這樣的疑問:擴充套件類載入器(ExtClassLoader)的父類載入器被強制設定為null了,那麼擴充套件類載入器為什麼還能將載入任務委派給啟動類載入器呢?
圖四標準擴充套件類載入器和系統類載入器成員大綱檢視
圖五 擴充套件類載入器和系統類載入器公共父類成員大綱檢視
通過圖四和圖五可以看出,標準擴充套件類載入器和系統類載入器及其父類(java.net.URLClassLoader和java.security.SecureClassLoader)都沒有覆寫java.lang.ClassLoader中預設的載入委派規則---loadClass(…)方法。有關java.lang.ClassLoader中預設的載入委派規則前面已經分析過,如果父載入器為null,則會呼叫本地方法進行啟動類載入嘗試。所以,圖三中,啟動類載入器、標準擴充套件類載入器和系統類載入器之間的委派關係事實上是仍就成立的。(在後面的使用者自定義類載入器部分,還會做更深入的分析)。
2.3 類載入雙親委派示例
以上已經簡要介紹了虛擬機器預設使用的啟動類載入器、標準擴充套件類載入器和系統類載入器,並以三者為例結合JDK程式碼對JVM預設使用的雙親委派類載入機制做了分析。下面我們就來看一個綜合的例子。首先在eclipse中建立一個簡單的java應用工程,然後寫一個簡單的JavaBean如下:
package classloader.test.bean;
publicclass TestBean {
public TestBean() {}
}
在現有當前工程中另外建立一測試類(ClassLoaderTest.java)內容如下:
測試一:
publicclass ClassLoaderTest {
publicstaticvoid main(String[] args) {
try {
//檢視當前系統類路徑中包含的路徑條目
System.out.println(System.getProperty("java.class.path"));
//呼叫載入當前類的類載入器(這裡即為系統類載入器)載入TestBean
Class typeLoaded = Class.forName("classloader.test.bean.TestBean");
//檢視被載入的TestBean型別是被那個類載入器載入的
System.out.println(typeLoaded.getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}
對應的輸出如下:
D:"DEMO"dev"Study"ClassLoaderTest"bin
(說明:當前類路徑預設的含有的一個條目就是工程的輸出目錄)
測試二:
將當前工程輸出目錄下的…/classloader/test/bean/TestBean.class打包進test.jar剪貼到< Java_Runtime_Home >/lib/ext目錄下(現在工程輸出目錄下和JRE擴充套件目錄下都有待載入型別的class檔案)。再執行測試一測試程式碼,結果如下:
D:"DEMO"dev"Study"ClassLoaderTest"bin
對比測試一和測試二,我們明顯可以驗證前面說的雙親委派機制,系統類載入器在接到載入classloader.test.bean.TestBean型別的請求時,首先將請求委派給父類載入器(標準擴充套件類載入器),標準擴充套件類載入器搶先完成了載入請求。
測試三:
將test.jar拷貝一份到< Java_Runtime_Home >/lib下,執行測試程式碼,輸出如下:
D:"DEMO"dev"Study"ClassLoaderTest"bin
測試三和測試二輸出結果一致。那就是說,放置到< Java_Runtime_Home >/lib目錄下的TestBean對應的class位元組碼並沒有被載入,這其實和前面講的雙親委派機制並不矛盾。虛擬機器出於安全等因素考慮,不會載入< Java_Runtime_Home >/lib存在的陌生類,開發者通過將要載入的非JDK自身的類放置到此目錄下期待啟動類載入器載入是不可能的。做個進一步驗證,刪除< Java_Runtime_Home >/lib/ext目錄下和工程輸出目錄下的TestBean對應的class檔案,然後再執行測試程式碼,則將會有ClassNotFoundException異常丟擲。有關這個問題,大家可以在java.lang.ClassLoader中的loadClass(String name, boolean resolve)方法中設定相應斷點執行測試三進行除錯,會發現findBootstrapClass0()會丟擲異常,然後在下面的findClass方法中被載入,當前執行的類載入器正是擴充套件類載入器(sun.misc.Launcher$ExtClassLoader),這一點可以通過JDT中變數檢視檢視驗證。
3 java程式動態擴充套件方式
Java的連線模型允許使用者執行時擴充套件引用程式,既可以通過當前虛擬機器中預定義的載入器載入編譯時已知的類或者介面,又允許使用者自行定義類裝載器,在執行時動態擴充套件使用者的程式。通過使用者自定義的類裝載器,你的程式可以裝載在編譯時並不知道或者尚未存在的類或者介面,並動態連線它們並進行有選擇的解析。
執行時動態擴充套件java應用程式有如下兩個途徑:
3.1 呼叫java.lang.Class.forName(…)
這個方法其實在前面已經討論過,在後面的問題2解答中說明了該方法呼叫會觸發那個類載入器開始載入任務。這裡需要說明的是多引數版本的forName(…)方法:
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
這裡的initialize引數是很重要的,可以覺得被載入同時是否完成初始化的工作(說明: 單引數版本的forName方法預設是不完成初始化的).有些場景下,需要將initialize設定為true來強制載入同時完成初始化,例如典型的就是利用DriverManager進行JDBC驅動程式類註冊的問題,因為每一個JDBC驅動程式類的靜態初始化方法都用DriverManager註冊驅動程式,這樣才能被應用程式使用,這就要求驅動程式類必須被初始化,而不單單被載入.
3.2 使用者自定義類載入器
通過前面的分析,我們可以看出,除了和本地實現密切相關的啟動類載入器之外,包括標準擴充套件類載入器和系統類載入器在內的所有其他類載入器我們都可以當做自定義類載入器來對待,唯一區別是是否被虛擬機器預設使用。前面的內容中已經對java.lang.ClassLoader抽象類中的幾個重要的方法做了介紹,這裡就簡要敘述一下一般使用者自定義類載入器的工作流程吧(可以結合後面問題解答一起看):
1、首先檢查請求的型別是否已經被這個類裝載器裝載到名稱空間中了,如果已經裝載,直接返回;否則轉入步驟2
2、委派類載入請求給父類載入器(更準確的說應該是雙親類載入器,真個虛擬機器中各種類載入器最終會呈現樹狀結構),如果父類載入器能夠完成,則返回父類載入器載入的Class例項;否則轉入步驟3
3、呼叫本類載入器的findClass(…)方法,試圖獲取對應的位元組碼,如果獲取的到,則呼叫defineClass(…)匯入型別到方法區;如果獲取不到對應的位元組碼或者其他原因失敗,返回異常給loadClass(…), loadClass(…)轉拋異常,終止載入過程(注意:這裡的異常種類不止一種)。
(說明:這裡說的自定義類載入器是指JDK 1.2以後版本的寫法,即不覆寫改變java.lang.loadClass(…)已有委派邏輯情況下)
4 常見問題分析:
4.1 由不同的類載入器載入的指定型別還是相同的型別嗎?
在Java中,一個類用其完全匹配類名(fully qualified class name)作為標識,這裡指的完全匹配類名包括包名和類名。但在JVM中一個類用其全名和一個載入類ClassLoader的例項作為唯一標識,不同類載入器載入的類將被置於不同的名稱空間.我們可以用兩個自定義類載入器去載入某自定義型別(注意,不要將自定義型別的位元組碼放置到系統路徑或者擴充套件路徑中,否則會被系統類載入器或擴充套件類載入器搶先載入),然後用獲取到的兩個Class例項進行java.lang.Object.equals(…)判斷,將會得到不相等的結果。這個大家可以寫兩個自定義的類載入器去載入相同的自定義型別,然後做個判斷;同時,可以測試載入java.*型別,然後再對比測試一下測試結果。
4.2 在程式碼中直接呼叫Class.forName(String name)方法,到底會觸發那個類載入器進行類載入行為?
Class.forName(String name)預設會使用呼叫類的類載入器來進行類載入。我們直接來分析一下對應的jdk的程式碼:
//java.lang.Class.java
publicstatic Class<?> forName(String className)throws ClassNotFoundException {
return forName0(className, true, ClassLoader.getCallerClassLoader());
}
//java.lang.ClassLoader.java
// Returns the invoker's class loader, or null if none.
static ClassLoader getCallerClassLoader() {
// 獲取呼叫類(caller)的型別
Class caller = Reflection.getCallerClass(3);
// This can be null if the VM is requesting it
if (caller == null) {
returnnull;
}
// 呼叫java.lang.Class中本地方法獲取載入該呼叫類(caller)的ClassLoader
return caller.getClassLoader0();
}
//java.lang.Class.java
//虛擬機器本地實現,獲取當前類的類載入器,前面介紹的Class的getClassLoader()也使用此方法
native ClassLoader getClassLoader0();
4.3 在編寫自定義類載入器時,如果沒有設定父載入器,那麼父載入器是?
前面講過,在不指定父類載入器的情況下,預設採用系統類載入器。可能有人覺得不明白,現在我們來看一下JDK對應的程式碼實現。眾所周知,我們編寫自定義的類載入器直接或者間接繼承自java.lang.ClassLoader抽象類,對應的無參預設建構函式實現如下:
//摘自java.lang.ClassLoader.java
protected ClassLoader() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
this.parent = getSystemClassLoader();
initialized = true;
}
我們再來看一下對應的getSystemClassLoader()方法的實現:
privatestaticsynchronizedvoid initSystemClassLoader() {
//...
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
scl = l.getClassLoader();
//...
}
我們可以寫簡單的測試程式碼來測試一下:
System.out.println(sun.misc.Launcher.getLauncher().getClassLoader());
本機對應輸出如下:
所以,我們現在可以相信當自定義類載入器沒有指定父類載入器的情況下,預設的父類載入器即為系統類載入器。同時,我們可以得出如下結論:
即時使用者自定義類載入器不指定父類載入器,那麼,同樣可以載入如下三個地方的類:
1. <Java_Runtime_Home>/lib下的類
2. < Java_Runtime_Home >/lib/ext下或者由系統變數java.ext.dir指定位置中的類
3. 當前工程類路徑下或者由系統變數java.class.path指定位置中的類
4.4 在編寫自定義類載入器時,如果將父類載入器強制設定為null,那麼會有什麼影響?如果自定義的類載入器不能載入指定類,就肯定會載入失敗嗎?
JVM規範中規定如果使用者自定義的類載入器將父類載入器強制設定為null,那麼會自動將啟動類載入器設定為當前使用者自定義類載入器的父類載入器(這個問題前面已經分析過了)。同時,我們可以得出如下結論:
即時使用者自定義類載入器不指定父類載入器,那麼,同樣可以載入到<Java_Runtime_Home>/lib下的類,但此時就不能夠載入<Java_Runtime_Home>/lib/ext目錄下的類了。
說明:問題3和問題4的推斷結論是基於使用者自定義的類載入器本身延續了java.lang.ClassLoader.loadClass(…)預設委派邏輯,如果使用者對這一預設委派邏輯進行了改變,以上推斷結論就不一定成立了,詳見問題5。
4.5 編寫自定義類載入器時,一般有哪些注意點?
1. 一般儘量不要覆寫已有的loadClass(…)方法中的委派邏輯
一般在JDK 1.2之前的版本才這樣做,而且事實證明,這樣做極有可能引起系統預設的類載入器不能正常工作。在JVM規範和JDK文件中(1.2或者以後版本中),都沒有建議使用者覆寫loadClass(…)方法,相比而言,明確提示開發者在開發自定義的類載入器時覆寫findClass(…)邏輯。舉一個例子來驗證該問題:
//使用者自定義類載入器WrongClassLoader.Java(覆寫loadClass邏輯)
publicclassWrongClassLoaderextends ClassLoader {
public Class<?> loadClass(String name) throws ClassNotFoundException {
returnthis.findClass(name);
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
//假設此處只是到工程以外的特定目錄D:/library下去載入類
具體實現程式碼省略
}
}
通過前面的分析我們已經知道,使用者自定義類載入器(WrongClassLoader)的默
認的類載入器是系統類載入器,但是現在問題4種的結論就不成立了。大家可以簡
單測試一下,現在<Java_Runtime_Home>/lib、< Java_Runtime_Home >/lib/ext和工
程類路徑上的類都載入不上了。
//問題5測試程式碼一
publicclass WrongClassLoaderTest {
publicstaticvoid main(String[] args) {
try {
WrongClassLoader loader = new WrongClassLoader();
Class classLoaded = loader.loadClass("beans.Account");
System.out.println(classLoaded.getName());
System.out.println(classLoaded.getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}
(說明:D:"classes"beans"Account.class物理存在的)
輸出結果:
java.io.FileNotFoundException: D:"classes"java"lang"Object.class (系統找不到指定的路徑。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at WrongClassLoader.findClass(WrongClassLoader.java:40)
at WrongClassLoader.loadClass(WrongClassLoader.java:29)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.lang.ClassLoader.defineClass(ClassLoader.java:400)
at WrongClassLoader.findClass(WrongClassLoader.java:43)
at WrongClassLoader.loadClass(WrongClassLoader.java:29)
at WrongClassLoaderTest.main(WrongClassLoaderTest.java:27)
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.lang.ClassLoader.defineClass(ClassLoader.java:400)
at WrongClassLoader.findClass(WrongClassLoader.java:43)
at WrongClassLoader.loadClass(WrongClassLoader.java:29)
at WrongClassLoaderTest.main(WrongClassLoaderTest.java:27)
這說明,連要載入的型別的超型別java.lang.Object都載入不到了。這裡列舉的由於覆寫loadClass(…)引起的邏輯錯誤明顯是比較簡單的,實際引起的邏輯錯誤可能複雜的多。
//問題5測試二
//使用者自定義類載入器WrongClassLoader.Java(不覆寫loadClass邏輯)
publicclassWrongClassLoaderextends ClassLoader {
protected Class<?> findClass(String name) throws ClassNotFoundException {
//假設此處只是到工程以外的特定目錄D:/library下去載入類
具體實現程式碼省略
}
}
將自定義類載入器程式碼WrongClassLoader.Java做以上修改後,再執行測試程式碼,輸出結果如下:
beans.Account
這說明,beans.Account載入成功,且是由自定義類載入器WrongClassLoader載入。
這其中的原因分析,我想這裡就不必解釋了,大家應該可以分析的出來了。
2. 2、正確設定父類載入器
通過上面問題4和問題5的分析我們應該已經理解,個人覺得這是自定義使用者類載入器時最重要的一點,但常常被忽略或者輕易帶過。有了前面JDK程式碼的分析作為基礎,我想現在大家都可以隨便舉出例子了。
3. 3、保證findClass(String )方法的邏輯正確性
事先儘量準確理解待定義的類載入器要完成的載入任務,確保最大程度上能夠獲取到對應的位元組碼內容。
4.6 如何在執行時判斷系統類載入器能載入哪些路徑下的類?
一是可以直接呼叫ClassLoader.getSystemClassLoader()或者其他方式獲取到系統類載入器(系統類載入器和擴充套件類載入器本身都派生自URLClassLoader),呼叫URLClassLoader中的getURLs()方法可以獲取到;
二是可以直接通過獲取系統屬性java.class.path 來檢視當前類路徑上的條目資訊 , System.getProperty("java.class.path")
4.7 如何在執行時判斷標準擴充套件類載入器能載入哪些路徑下的類?
方法之一:
try {
URL[] extURLs = ((URLClassLoader)ClassLoader.getSystemClassLoader().getParent()).getURLs();
for (int i = 0; i < extURLs.length; i++) {
System.out.println(extURLs[i]);
}
} catch (Exception e) {//…}
本機對應輸出如下:
file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/dnsns.jar
file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/localedata.jar
file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/sunjce_provider.jar
file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar
5 總結:
寫這篇文章的初衷是通過分析JDK相關程式碼來驗證一些載入規則,核心就是藉助雙親委派機制來分析一個載入請求處理的主要過程,所列舉的幾個簡單的例子實際意義不大,因為遇到的情況往往比例子情況複雜的多。
下一篇文章,我會重點分析一下Eclipse的外掛類載入器,並分析一下外掛環境下的類載入和普通java應用場景中的類載入有什麼不同,並會提供一個比較完整的類載入器。
外掛類載入器就是一個由eclipse開發的一個使用者自定義類載入器,所以分析時候用到的一些基本的東西都是在這篇文章中涉及到的。
這篇文章寫的時候時間比較緊,亂糟糟的,大家見諒。中間參考了JVM規範、jdk文件和程式碼、《Inside Java Virtual Machine》一書等資料。
文章中肯定包含了一些錯誤,歡迎指出,謝謝!
相關推薦
Java類載入原理解析
每個java開發人員對java.lang.ClassNotFoundExcetpion這個異常肯定都不陌生,這背後就涉及到了java技術體系中的類載入。Java的類載入機制是java技術體系中比較核心的部分,雖然和大部分開發人員直接打交道不多,但是對其背後的機理有一定理解有
weblogic與Java類載入器原理試驗解析
Public class Test1{ Public static void main(String[] arg){ System.out.println(Test1.class.getClassLoader()); Test2 t2 = new Test2(); T2.
Java類載入器原理解析
1 JVM有三種預定義型別類載入器 啟動類載入器:引導類裝入器是用原生代碼實現的類裝入器,它負責將 <Java_Runtime_Home>/lib下面的核心類庫或-Xbootcl
Java類載入器 ClassLoader的解析
index html dir obj ble body 6.4 odin 普通 //參考 : http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 類載入器基本概念 類載
Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例
【正文】Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5. 執行緒上下文
[五]類載入機制雙親委派機制 底層程式碼實現原理 原始碼分析 java類載入雙親委派機制是如何實現的
Launcher啟動類 本文是雙親委派機制的原始碼分析部分,類載入機制中的雙親委派模型對於jvm的穩定執行是非常重要的 不過原始碼其實比較簡單,接下來簡單介紹一下 我們先從啟動類說起 有一個Launcher類 sun.misc.Launcher; 仔細看下這簡
java類載入器——ClassLoader
web rac rgb 好的 全盤負責機制 安全 trac 字節 如何 Java的設計初衷是主要面向嵌入式領域,對於自己定義的一些類,考慮使用依需求載入原則。即在程序使用到時才載入類,節省內存消耗,這時就可以通過類載入器來動態載入。 假設你平時僅僅是做web開發,那應該
JAVA類載入器詳解
Java類載入器的作用就是在執行時載入類。Java類載入器基於三個機制:委託、可見性和單一性。委託機制是指將載入一個類的請求交給父類載入器,如果這個父類載入器不能夠找到或者載入這個類,那麼再載入它。可見性的原理是子類的載入器可以看見所有的父類載入器載入的類,而父類載入器看不到子類載入器載入的
1.java類載入器?
Java類載入器ClassLoader總結 JAVA類裝載方式,有兩種: 1.隱式裝載, 程式在執行過程中當碰到通過new 等方式生成物件時,隱式呼叫類裝載器載入對應的類到jvm中。 2.顯式裝載, 通過class.forname()等方法,顯式載入需要的類 類載
Java類載入器(死磕5)
Java類載入器( CLassLoader ) 死磕5: 自定義一個檔案系統classLoader 本小節目錄 5.1. 自定義類載入器的基本流程 5.2. 入門案例:自定義檔案系統類載入器 5.3. 案例的環境配置 5.4 FileClassLoader
從阿里巴巴面試題到java類載入機制
首先很經典的阿里巴巴面試題 加上我自己的一些疑惑程式碼 public class Text { public static int k = 0; public final int k1 = 3; //自己加的 public static Text t1 = new Text("
Java類載入器( 死磕9)
【正文】Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5. 執行緒上下文
Java類載入器( 死磕7)
【正文】Java類載入器( CLassLoader )死磕7: 基於加密的自定義網路載入器 本小節目錄 7.1. 加密傳輸Server端的原始碼 7.2. 加密傳輸Client端的原始碼 7.3. 使用亦或實現簡單加密和解密演算法 7. 網路加密SafeClassLoader的原始
Java類載入器( 死磕 4)
【正文】Java類載入器( CLassLoader ) 死磕 之4: 神祕的雙親委託機制 本小節目錄 4.1. 每個類載入器都有一個parent父載入器 4.2. 類載入器之間的層次關係 4.3. 類的載入次序 4.4 雙親委託機制原理與沙箱機制 4.5. forName
Java類載入器(死磕3)
【正文】Java類載入器( CLassLoader ) 死磕3: 揭祕 ClassLoader抽象基類 本小節目錄 3.1. 類的載入分類:隱式載入和顯示載入 3.2. 載入一個類的五步工作 3.3. 如何獲取類的載入器 3.4 解刨載入器——ClassLoade
Java類載入器(死磕 1-2)
Java類載入器( CLassLoader ) 死磕 1、2: 匯入 & 類載入器分類 本小節目錄 1.匯入 1.1. 從class檔案的載入開始 1.2. 什麼是類載入器 2. JAVA類載入器分類 2.1. 作業系統的環境變數 2.2. Bo
Java類載入器( 深磕8)
【正文】Java類載入器( CLassLoader ) 深磕 8: 使用ASM,和類載入器實現AOP 本小節目錄 8.1. ASM位元組碼操作框架簡介 8.2. ASM和訪問者模式 8.3. 用於增強位元組碼的事務類 8.4 通過ASM訪問註解 8.5. 通過ASM注入
Java類載入器( 深磕9)
【正文】Java類載入器( CLassLoader ) 深磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5.
Java類載入器( 深磕7)
【正文】Java類載入器( CLassLoader )深磕7: 基於加密的自定義網路載入器 本小節目錄 7.1. 加密傳輸Server端的原始碼 7.2. 加密傳輸Client端的原始碼 7.3. 使用亦或實現簡單加密和解密演算法 7. 網路加密SafeClassLoa
Java類載入器( 深磕 6)
【正文】Java類載入器( CLassLoader )深磕 6: 自定義網路類載入器 本小節目錄 6.1. 自定義網路類載入器的類設計 6.2. 檔案傳輸Server端的原始碼 6.3. 檔案傳輸Client端的原始碼 6. 4 自定義載入器SocketClassLoade