From:深入理解Java虛擬機(jī)
- 目錄
BiBi - JVM -0- 開(kāi)篇
BiBi - JVM -1- Java內(nèi)存區(qū)域
BiBi - JVM -2- 對(duì)象
BiBi - JVM -3- 垃圾收集算法
BiBi - JVM -4- HotSpot JVM
BiBi - JVM -5- 垃圾回收器
BiBi - JVM -6- 回收策略
BiBi - JVM -7- Java類(lèi)文件結(jié)構(gòu)
BiBi - JVM -8- 類(lèi)加載機(jī)制
BiBi - JVM -9- 類(lèi)加載器
BiBi - JVM -10- 虛擬機(jī)字節(jié)碼
BiBi - JVM -11- 編譯期優(yōu)化
BiBi - JVM -12- 運(yùn)行期優(yōu)化
BiBi - JVM -13- 并發(fā)
類(lèi)加載器定義
虛擬機(jī)在類(lèi)加載階段中的【通過(guò)一個(gè)類(lèi)的全限定名來(lái)獲取描述此類(lèi)的二進(jìn)制字節(jié)流】命咐,這個(gè)過(guò)程放到Java虛擬機(jī)外部去實(shí)現(xiàn)糜芳,以便讓?xiě)?yīng)用程序自己決定如何去獲取所需要的類(lèi)霍掺,實(shí)現(xiàn)這個(gè)過(guò)程的代碼模塊稱(chēng)為【類(lèi)加載器】赌蔑。
類(lèi)加載器最初是為了滿(mǎn)足Java Applet的需求而開(kāi)發(fā)的,現(xiàn)在Java Applet技術(shù)已經(jīng)落伍了搔确,但類(lèi)加載器技術(shù)卻得到了廣泛應(yīng)用彼棍。如:類(lèi)層次劃分、OSGi膳算、熱部署座硕、代碼加密等領(lǐng)域。
任意一個(gè)類(lèi)涕蜂,都需要由加載它的類(lèi)加載器和這個(gè)類(lèi)本身华匾,共同確定其在Java虛擬機(jī)中的唯一性。每一個(gè)類(lèi)加載器机隙,都有一個(gè)獨(dú)立的類(lèi)名空間蜘拉。即萨西,判斷類(lèi)是否相等、instanceof是基于同一個(gè)類(lèi)加載器而言的旭旭。
小例子:
Object obj = myLoader.loadClass("com.ljg.Test").newInstance();
System.out.println(obj.getClass()); //com.ljg.Test
System.out.println(obj instanceof com.ljg.Test); //false
盡管obj.getClass()
返回com.ljg.Test谎脯,但instanceof結(jié)果為false。因?yàn)樘摂M機(jī)中存在兩個(gè)Test類(lèi)持寄,一個(gè)由系統(tǒng)應(yīng)用程序類(lèi)加載器加載的源梭,另一個(gè)由我們自定義的myLoader類(lèi)加載器加載的,雖然都來(lái)自同一個(gè)Class文件稍味,但依然是兩個(gè)獨(dú)立的類(lèi)废麻。
雙親委派模型
從Java虛擬機(jī)角度講,加載器分兩種:
1)啟動(dòng)類(lèi)加載器仲闽,使用C++語(yǔ)言實(shí)現(xiàn)【針對(duì)HotSpot而言】,是虛擬機(jī)自身的一部分僵朗。
2)其它的類(lèi)加載器赖欣,使用Java語(yǔ)言實(shí)現(xiàn),獨(dú)立于虛擬機(jī)之外验庙,并且都繼承抽象類(lèi)ClassLoader顶吮。從Java開(kāi)發(fā)人員角度講,有三種重要的類(lèi)加載器:
1)啟動(dòng)類(lèi)加載器
加載<java_home>\lib目錄中的粪薛,并且是虛擬機(jī)識(shí)別的類(lèi)悴了。啟動(dòng)類(lèi)加載器無(wú)法被Java程序直接引用。
2)擴(kuò)展類(lèi)加載器
由ExtClassLoader實(shí)現(xiàn)违寿,加載<java_home>\lib\ext目錄中的類(lèi)湃交,開(kāi)發(fā)者可以直接使用擴(kuò)展類(lèi)加載器。
3)應(yīng)用程序類(lèi)加載器
由AppClassLoader實(shí)現(xiàn)藤巢,是getSystemClassLoader()方法的返回值搞莺,所以一般稱(chēng)為【系統(tǒng)類(lèi)加載器】。他負(fù)責(zé)加載用戶(hù)類(lèi)路徑上所定義的類(lèi)掂咒,開(kāi)發(fā)者可以直接使用這個(gè)類(lèi)加載器才沧。如果用戶(hù)沒(méi)有自定義類(lèi)加載器,會(huì)默認(rèn)使用個(gè)這個(gè)類(lèi)加載器绍刮。
雙親委派模型的過(guò)程:如果一個(gè)類(lèi)加載器收到了類(lèi)加載的請(qǐng)求温圆,它首先不會(huì)自己去加載這個(gè)類(lèi),而是把這個(gè)請(qǐng)求委派給父類(lèi)加載器去完成孩革,每一層的類(lèi)加載器都是如此岁歉,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到頂層的【啟動(dòng)類(lèi)加載器】,只有當(dāng)父加載器反饋?zhàn)约簾o(wú)法完成這個(gè)加載請(qǐng)求時(shí)【即它的搜索范圍中沒(méi)有找到所需的類(lèi)】膝蜈,子加載器才會(huì)嘗試自己去加載刨裆。
注意:類(lèi)加載器之間的父子關(guān)系一般不是繼承澈圈,而是使用組合關(guān)系來(lái)復(fù)用父加載器。
雙親委派模型的好處
Java類(lèi)具有一種優(yōu)先級(jí)的層次關(guān)系帆啃。如:java.lang.Object瞬女,無(wú)論哪個(gè)類(lèi)加載器要加載這個(gè)類(lèi),最終都會(huì)委派給處于模型最頂端的啟動(dòng)類(lèi)加載器進(jìn)行加載努潘,因此Object類(lèi)在程序的各種類(lèi)加載器環(huán)境中都是同一個(gè)類(lèi)诽偷。
注意:如果自己定義的類(lèi)與rt.jar類(lèi)庫(kù)中類(lèi)重名【權(quán)限的類(lèi)名相同】,將會(huì)正常編譯疯坤,但是自己定義的類(lèi)將無(wú)法被加載運(yùn)行报慕。
雙親委派偽代碼
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
//首先,檢查請(qǐng)求的類(lèi)是否已經(jīng)被加載過(guò)了
Class c = findLocalClass(name);
if (null == c) {
try {
if (null != parent) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//如果父類(lèi)加載器拋出異常压怠,說(shuō)明父類(lèi)加載器無(wú)法完成加載請(qǐng)求
}
if (null == c) {
//父類(lèi)無(wú)法加載時(shí)眠冈,調(diào)用本身的findClass方法來(lái)進(jìn)行加載
c = findClass(name);
}
if (resolve) {
resolveClass(c);
}
}
return c;
}
破壞雙親委派模型
問(wèn)題:雙親委派很好地解決了各個(gè)類(lèi)加載器的基礎(chǔ)類(lèi)的統(tǒng)一問(wèn)題【越基礎(chǔ)的類(lèi)由越上層加載器進(jìn)行加載】,但若基礎(chǔ)類(lèi)要回調(diào)用戶(hù)的代碼菌瘫,該怎么辦蜗顽?如:在JNDI服務(wù)中,它的代碼由啟動(dòng)類(lèi)加載器去加載雨让,但JNDI的目的是對(duì)資源進(jìn)行集中管理和查找雇盖,它需要調(diào)用由獨(dú)立廠商實(shí)現(xiàn)并部署在應(yīng)用程序的ClassPath下的JNDI接口提供者的代碼,但啟動(dòng)類(lèi)加載器不可能認(rèn)識(shí)這些代碼栖忠。
解答:Java提供了【線程上下文類(lèi)加載器】崔挖,通過(guò)setContextClassLoader()方法進(jìn)行設(shè)置,如果創(chuàng)建線程時(shí)沒(méi)有設(shè)置庵寞,將從父類(lèi)中繼承狸相,如果在應(yīng)用程序全局范圍內(nèi)都沒(méi)有設(shè)置的話,那這個(gè)類(lèi)加載器默認(rèn)是應(yīng)用程序類(lèi)加載器捐川。通過(guò)【線程上下文類(lèi)加載器】父類(lèi)加載器可以請(qǐng)求子類(lèi)加載器去完成類(lèi)加載的動(dòng)作卷哩。這種行為實(shí)際上就是通過(guò)了雙親委派模型的層次結(jié)構(gòu)來(lái)逆向使用類(lèi)加載器。
-
動(dòng)態(tài)性追求導(dǎo)致雙親委派模型被破壞
如:熱更新【Hot Swap】属拾、熱部署【Hot Deployment】
模塊化熱部署的關(guān)鍵是它自定義的類(lèi)加載器機(jī)制的實(shí)現(xiàn)将谊,每一個(gè)程序模塊都有一個(gè)自己的類(lèi)加載器,當(dāng)需要更換一個(gè)Bundle時(shí)渐白,就把Bundle連同類(lèi)加載器一起更換以實(shí)現(xiàn)代碼的熱更新尊浓。
Tomcat中,JasperLoader的加載范圍僅僅是JSP文件所編譯出來(lái)的一個(gè)Class纯衍,它出現(xiàn)的目的就是被丟棄:當(dāng)服務(wù)器檢測(cè)到JSP文件被修改時(shí)栋齿,會(huì)替換掉目前的JasperLoader的實(shí)例,并通過(guò)再建立一個(gè)新的Jsp類(lèi)加載器來(lái)實(shí)現(xiàn)JSP文件的HotSwap功能。