1. ClassLoader源碼
??Java中的所有類(lèi)窥浪,必須被裝載到j(luò)vm中才能運(yùn)行刚陡,類(lèi)裝載器把類(lèi)文件從硬盤(pán)讀取到內(nèi)存中唆鸡,JVM在加載類(lèi)的時(shí)候趾诗,都是通過(guò)ClassLoader的loadClass()來(lái)加載class的痹届,loadClass使用雙親委派模式贮尖。
ClassLoader抽象類(lèi):
public abstract class ClassLoader
ClassLoader類(lèi)是一個(gè)抽象類(lèi)笛粘,sun公司是這么解釋這個(gè)類(lèi)的:
class loader是一個(gè)負(fù)責(zé)加載classes的對(duì)象,ClassLoader類(lèi)是一個(gè)抽象類(lèi),需要給出類(lèi)的二進(jìn)制名稱薪前,class loader嘗試定位或者產(chǎn)生一個(gè)class的數(shù)據(jù)润努,一個(gè)典型的策略是把二進(jìn)制名字轉(zhuǎn)換成文件名然后到文件系統(tǒng)中找到該文件。
loadClass():
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
??使用指定的二進(jìn)制名稱(name)來(lái)加載類(lèi)示括,默認(rèn)實(shí)現(xiàn)按照以下順序查找類(lèi):
- 1.getClassLoadingLock(name)獲取本實(shí)例的鎖
- 1.調(diào)用findLoadedClass(String)方法檢查這個(gè)類(lèi)是否被加載過(guò)
- 2.使用父加載器調(diào)用loadClass(String)方法
- 3.如果父加載器為Null铺浇,類(lèi)加載器裝載虛擬機(jī)內(nèi)置的加載器調(diào)用findClass(String)方法裝載類(lèi)
- 4.如果,按照以上的步驟成功的找到對(duì)應(yīng)的類(lèi)垛膝,并且該方法接收的resolve參數(shù)的值為true,那么就調(diào)用resolveClass(Class)方法來(lái)處理類(lèi)鳍侣。
- 5.ClassLoader的子類(lèi)最好覆蓋findClass(String)而不是這個(gè)方法。 除非被重寫(xiě)吼拥,這個(gè)方法默認(rèn)在整個(gè)裝載過(guò)程中都是同步的(線程安全的).
1.1 synchronized (getClassLoadingLock(name))
??這是一個(gè)同步代碼塊倚聚,synchronized的括號(hào)中放的應(yīng)該是一個(gè)對(duì)象,getClassLoadingLock(name)方法:
protected Object getClassLoadingLock(String className) {
Object lock = this;
if (parallelLockMap != null) {
Object newLock = new Object();
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
??根據(jù)變量parallelLockMap的值進(jìn)行不同的操作凿可,如果這個(gè)變量是Null則直接返回this惑折,如果這個(gè)屬性不為Null則新建一個(gè)對(duì)象,然后在調(diào)用一個(gè)putIfAbsent(className, newLock);方法來(lái)給剛剛創(chuàng)建好的對(duì)象賦值枯跑,這個(gè)方法的作用我們一會(huì)講惨驶。而parallelLockMap變量又是ClassLoader類(lèi)的成員變量:
private final ConcurrentHashMap<String, Object> parallelLockMap;
??parallelLockMap的初始化是在ClassLoader的構(gòu)造方法里做的:
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
??構(gòu)造函數(shù)根據(jù)一個(gè)屬性ParallelLoaders的Registered狀態(tài)的不同來(lái)給parallelLockMap賦值。ParallelLoaders又是在哪賦值的呢敛助?在ClassLoader類(lèi)中包含一個(gè)靜態(tài)內(nèi)部類(lèi)private static class ParallelLoaders粗卜,在ClassLoader被加載的時(shí)候這個(gè)靜態(tài)內(nèi)部類(lèi)就被初始化。
private static class ParallelLoaders {
private ParallelLoaders() {}
// the set of parallel capable loader types
private static final Set<Class<? extends ClassLoader>> loaderTypes =
Collections.newSetFromMap(
new WeakHashMap<Class<? extends ClassLoader>, Boolean>());
static {
synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); }
}
/**
* Registers the given class loader type as parallel capabale.
* Returns {@code true} is successfully registered; {@code false} if
* loader's super class is not registered.
*/
static boolean register(Class<? extends ClassLoader> c) {
synchronized (loaderTypes) {
if (loaderTypes.contains(c.getSuperclass())) {
// register the class loader as parallel capable
// if and only if all of its super classes are.
// Note: given current classloading sequence, if
// the immediate super class is parallel capable,
// all the super classes higher up must be too.
loaderTypes.add(c);
return true;
} else {
return false;
}
}
}
/**
* Returns {@code true} if the given class loader type is
* registered as parallel capable.
*/
static boolean isRegistered(Class<? extends ClassLoader> c) {
synchronized (loaderTypes) {
return loaderTypes.contains(c);
}
}
}
??這個(gè)靜態(tài)類(lèi)ParallelLoaders封裝了并行的可裝載的類(lèi)型的集合纳击。
- 首先,在ClassLoader類(lèi)中有一個(gè)靜態(tài)內(nèi)部類(lèi)ParallelLoaders评疗,他會(huì)指定的類(lèi)的并行能力测砂。
- 如果當(dāng)前的加載器被定位為具有并行能力,那么他就給parallelLockMap定義百匆,就是new一個(gè) ConcurrentHashMap<>()砌些,那么這個(gè)時(shí)候,我們知道如果當(dāng)前的加載器是具有并行能力的加匈,那么parallelLockMap就不是Null存璃。
- 這個(gè)時(shí)候,我們判斷parallelLockMap是不是Null雕拼,如果他是null纵东,說(shuō)明該加載器沒(méi)有注冊(cè)并行能力,那么我們沒(méi)有必要給他一個(gè)加鎖的對(duì)象啥寇,getClassLoadingLock方法直接返回this,就是當(dāng)前的加載器的一個(gè)實(shí)例偎球。
- 如果這個(gè)parallelLockMap不是null洒扎,那就說(shuō)明該加載器是有并行能力的,那么就可能有并行情況衰絮,那就需要返回一個(gè)鎖對(duì)象袍冷。然后就是創(chuàng)建一個(gè)新的Object對(duì)象,調(diào)用parallelLockMap的putIfAbsent(className, newLock)方法猫牡。
- putIfAbsent(className, newLock)的作用是:首先根據(jù)傳進(jìn)來(lái)的className,檢查該名字是否已經(jīng)關(guān)聯(lián)了一個(gè)value值胡诗,如果已經(jīng)關(guān)聯(lián)過(guò)value值,那么直接把他關(guān)聯(lián)的值返回淌友,如果沒(méi)有關(guān)聯(lián)過(guò)值的話煌恢,那就把我們傳進(jìn)來(lái)的Object對(duì)象作為value值,className作為Key值組成一個(gè)map返回震庭。然后無(wú)論putIfAbsent方法的返回值是什么瑰抵,都把它賦值給我們剛剛生成的那個(gè)Object對(duì)象。
- 這個(gè)時(shí)候归薛,我們來(lái)簡(jiǎn)單說(shuō)明一下getClassLoadingLock(String className)的作用,就是: 為類(lèi)的加載操作返回一個(gè)鎖對(duì)象匪蝙。為了向后兼容主籍,這個(gè)方法這樣實(shí)現(xiàn):如果當(dāng)前的classloader對(duì)象注冊(cè)了并行能力,方法返回一個(gè)與指定的名字className相關(guān)聯(lián)的特定對(duì)象逛球,否則千元,直接返回當(dāng)前的ClassLoader對(duì)象。
1.2 findLoadedClass(name)
??在加載類(lèi)之前先調(diào)用findLoadedClass方法檢查該類(lèi)是否已經(jīng)被加載過(guò)颤绕,findLoadedClass會(huì)返回一個(gè)Class類(lèi)型的對(duì)象幸海,如果該類(lèi)已經(jīng)被加載過(guò),那么就可以直接返回該對(duì)象(在返回之前會(huì)根據(jù)resolve的值來(lái)決定是否處理該對(duì)象奥务,具體的怎么處理后面會(huì)講)物独。 如果,該類(lèi)沒(méi)有被加載過(guò)氯葬,那么執(zhí)行以下的加載過(guò)程挡篓。
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
??如果父加載器不為空,那么調(diào)用父加載器的loadClass方法加載類(lèi)帚称,如果父加載器為空官研,那么調(diào)用虛擬機(jī)的加載器來(lái)加載類(lèi)。如果以上兩個(gè)步驟都沒(méi)有成功的加載到類(lèi)闯睹,那么調(diào)用自己的findClass(name)方法來(lái)加載類(lèi):
c = findClass(name);
??這個(gè)時(shí)候戏羽,我們已經(jīng)得到了加載之后的類(lèi),那么就根據(jù)resolve的值決定是否調(diào)用resolveClass方法楼吃。resolveClass方法的作用是:
鏈接指定的類(lèi)始花。這個(gè)方法給Classloader用來(lái)鏈接一個(gè)類(lèi)妄讯,如果這個(gè)類(lèi)已經(jīng)被鏈接過(guò)了,那么這個(gè)方法只做一個(gè)簡(jiǎn)單的返回衙荐。否則捞挥,這個(gè)類(lèi)將被按照 Java?規(guī)范中的Execution描述進(jìn)行鏈接。
1.3 類(lèi)加載器
??java中的類(lèi)大致分為三種:系統(tǒng)類(lèi)忧吟、擴(kuò)展類(lèi)砌函、由程序員自定義的類(lèi)。類(lèi)裝載方式包括隱式裝載和顯式裝載溜族。隱式裝載:程序在運(yùn)行過(guò)程中當(dāng)碰到通過(guò)new 等方式生成對(duì)象時(shí)讹俊,隱式調(diào)用類(lèi)裝載器加載對(duì)應(yīng)的類(lèi)到j(luò)vm中。顯式裝載:通過(guò)class.forname()等方法煌抒,顯式加載需要的類(lèi)仍劈。
??類(lèi)加載的動(dòng)態(tài)性是指:一個(gè)應(yīng)用程序總是由n多個(gè)類(lèi)組成,Java程序啟動(dòng)時(shí)寡壮,并不是一次把所有的類(lèi)全部加載后再運(yùn)行贩疙,它總是先把保證程序運(yùn)行的基礎(chǔ)類(lèi)一次性加載到j(luò)vm中,其它類(lèi)等到j(luò)vm用到的時(shí)候再加載况既,這樣的好處是節(jié)省了內(nèi)存的開(kāi)銷(xiāo)这溅,因?yàn)閖ava最早就是為嵌入式系統(tǒng)而設(shè)計(jì)的,內(nèi)存寶貴棒仍,這是一種可以理解的機(jī)制悲靴,而用到時(shí)再加載這也是java動(dòng)態(tài)性的一種體現(xiàn)。
??Java中的類(lèi)裝載器實(shí)質(zhì)上也是類(lèi)莫其,功能是把類(lèi)載入jvm中癞尚,值得注意的是jvm的類(lèi)裝載器并不是一個(gè),而是三個(gè)乱陡,層次結(jié)構(gòu)如下:
??為什么要有三個(gè)類(lèi)加載器浇揩,一方面是分工,各自負(fù)責(zé)各自的區(qū)塊憨颠,另一方面為了實(shí)現(xiàn)委托模型临燃,下面會(huì)談到該模型。
??類(lèi)加載器的工作原理:在這里java采用了委托模型機(jī)制烙心,這個(gè)機(jī)制簡(jiǎn)單來(lái)講膜廊,就是“類(lèi)裝載器有載入類(lèi)的需求時(shí),會(huì)先請(qǐng)示其Parent使用其搜索路徑幫忙載入淫茵,如果Parent 找不到,那么才由自己依照自己的搜索路徑搜索類(lèi)”爪瓜。
示例:
Public class Test{
Public static void main(String[] arg){
ClassLoader c = Test.class.getClassLoader(); //獲取Test類(lèi)的類(lèi)加載器
System.out.println(c);
ClassLoader c1 = c.getParent(); //獲取c這個(gè)類(lèi)加載器的父類(lèi)加載器
System.out.println(c1);
ClassLoader c2 = c1.getParent();//獲取c1這個(gè)類(lèi)加載器的父類(lèi)加載器
System.out.println(c2);
}
}
結(jié)果:
。匙瘪。铆铆。AppClassLoader蝶缀。。薄货。
翁都。。谅猾。ExtClassLoader柄慰。。税娜。
Null
??Test是由AppClassLoader加載器加載的坐搔,AppClassLoader的Parent 加載器是 ExtClassLoader,但是ExtClassLoader的Parent為 null 是怎么回事呵,朋友們留意的話敬矩,前面有提到Bootstrap Loader是用C++語(yǔ)言寫(xiě)的概行,依java的觀點(diǎn)來(lái)看,邏輯上并不存在Bootstrap Loader的類(lèi)實(shí)體弧岳,所以在java程序代碼里試圖打印出其內(nèi)容時(shí)凳忙,我們就會(huì)看到輸出為null。
??類(lèi)裝載器ClassLoader(一個(gè)抽象類(lèi))描述一下JVM加載class文件的原理機(jī)制禽炬。類(lèi)裝載器就是尋找類(lèi)或接口字節(jié)碼文件進(jìn)行解析并構(gòu)造JVM內(nèi)部對(duì)象表示的組件涧卵,在java中類(lèi)裝載器把一個(gè)類(lèi)裝入JVM,經(jīng)過(guò)以下步驟:
1瞎抛、裝載:查找和導(dǎo)入Class文件
2艺演、鏈接:其中解析步驟是可以選擇的 (a)檢查:檢查載入的class文件數(shù)據(jù)的正確性 (b)準(zhǔn)備:給類(lèi)的靜態(tài)變量分配存儲(chǔ)空間 (c)解析:將符號(hào)引用轉(zhuǎn)成直接引用 3却紧、初始化:對(duì)靜態(tài)變量桐臊,靜態(tài)代碼塊執(zhí)行初始化工作