類加載機制
加載是指查找字節流,並且據此創建類的過程。加載需要藉助類加載器,在 Java 虛擬機中,類加載器使用了雙親委派模型,即接收到加載請求時,會先將請求轉發給父類加載器。
ClassLoader雙親委派機制
類加載器加載類步驟:
- 首先會自底向上檢測是否加載過此Class(即在緩存區中是否有此Class),如果有直接返回Class對象;
- 判斷父加載器是否存在,如果存在父加載器,則請求父類加載器來載入此Class,如果成功則返回Class對象;如果不成功則使用子類加載器來載入此Class,如果成功則返回Class對象,如果不成功則拋出ClassNotFoundException()異常;
- 如果不存在父類加載器(要麼父類加載器是根加載器;要麼本身就是根加載器),請求根類加載器是來載入此Class,如果成功則返回Class對象,如果不成功則拋出ClassNotFoundException()異常;
源碼淺析(關鍵方法)
loadClass(String name, boolean resolve)
name是類的名字,resolve是解析
404行會進入一個同步鎖,因爲有可能涉及到多個線程調用一個ClassLoader加載同一個類,保證線程安全,避免衝突;
406行Class<?> c = findLoadedClass(name);
會尋找曾經是否加載過目標Class,如果有會直接跳過407的if
和432的if
,直接return c;
,即返回目標Class對象;
如果沒有加載過目標Class,會直接進入407行的 if
,408行會進入計時(可以忽略);則會判斷parent
是否爲空,如果parent
不爲null
,那麼當前ClassLoader的父加載器,再次進入loadClass
方法即401行;
如果當父加載器是BootStrapClassLoader
時或者自己本身就是BootStrapClassLoader
,由於BootStrapClassLoader
是C++編寫的,parent
則會返回null
;
進入412行的else
, c = findBootstrapClassOrNull(name);
去查找BootStrapClassLoader
有沒有載入過目標Class;若載入過,返回Class對象;否則會進入BootStrapClassLoader
目錄下掃描是有目標Class,如果有返回,沒有就會返回;
進入420行的if
, c = findClass(name);
會在當前ClassLoader目錄中去查找是否有目標Class;如果有就裝載目標Class並返回,如果沒有就會輪到子類ClassLoader來查找,最終會到自定義ClassLoader去查找是否有目標Class,如果有返回,沒有就會拋出ClassNotFoundException()異常;
ClassLoader之間的關係
package ClassLoader;
public class ClassLoaderDemo {
public static void main(String[] args) {
Class c = ClassLoaderDemo.class;
//由於是使用系統類加載器(非自定義加載器)來加載ClassLoaderDemo類,應該輸出AppClassLoader
System.out.println(c.getClassLoader());
//AppClassLoader的父加載器應該是ExtClassLoader
System.out.println(c.getClassLoader().getParent());
//ExtClassLoader的父加載器應該是BootStrapClassLoader(BootStrapClassLoader應該輸出null)
System.out.println(c.getClassLoader().getParent().getParent());
}
}
自定義ClassLoader——>AppClassLoader——>ExtClassLoader——>BootStrapClassLoader(null)
Tips:爲什麼BootStrapClassLoader是null?
在上面的413行代碼中提到c = findBootstrapClassOrNull(name);
native
會去調用本地庫或者外置都非Java代碼(在openjdk中可以查看)
爲什麼要使用雙親委派機制加載類?
避免多份同樣字節碼重複加載,內存是有限的,沒有必要保存相同的Class對象。
例如:System.out.println();
類A打印時會加載一次System類;類B打印時又會加載一次System類,那麼內存中就會存在兩份相同的System字節碼,造成沒必要的內存消耗;
如果使用雙親委派機制加載類,加載過後就不會再重複加載相同的Class;