JVM類加載器
@(Java)[JVM|類加載器]
類加載過程中的加載階段在JVM的外部實(shí)現(xiàn)。這樣做可以讓應(yīng)用程序自己決定如何去獲取所需要的類。實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊就是類加載器。
[TOC]
類與類加載器的關(guān)系
對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確定該類在JVM中的唯一性。即比較兩個(gè)類是否相等,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提下才有意義,否則,即使這兩個(gè)類來源于同一個(gè)Class文件,只要加載它們的類加載器不同,那么這兩個(gè)類必定不相等。
- 這里的“相等”:包括代表類的Class對(duì)象的equals方法、isAssignableFrom方法和isInstance方法的返回結(jié)果,也包括了使用instanceof關(guān)鍵字做對(duì)象所屬關(guān)系判定等情況。
package ThinkingInJVM;
import java.io.IOException;
import java.io.InputStream;
public class ClassLoadTest {
public static void main(String args[])throws Exception{
ClassLoader myLoad=new ClassLoader() {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
try {
String fileName=name.substring(name.lastIndexOf(".")+1)+".class";
InputStream is=getClass().getResourceAsStream(fileName);
if (is==null){
return super.loadClass(name);
}
byte[] b=new byte[is.available()];
is.read(b);
return defineClass(name,b,0,b.length);
}
catch (IOException e){
throw new ClassNotFoundException();
}
}
};
Object obj=myLoad.loadClass("ThinkingInJVM.ClassLoadTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof ThinkingInJVM.ClassLoadTest);
}
}
在上述代碼中,創(chuàng)建了myLoad對(duì)象,并重寫了類加載器的loadClass方法,構(gòu)造了一個(gè)自定義的類加載器。在最后的比較中,雖然obj.getClass()返回了ThinkingInJVM.ClassLoadTest,但在instanceof的比較中,結(jié)果是false。
- 這是因?yàn)镴VM中存在兩個(gè)ThinkingInJVM.ClassLoadTest類,一個(gè)是由系統(tǒng)類加載器加載的,另一個(gè)是由自定義的myLoad類加載器加載的。雖然來自同一個(gè)Class文件,但依舊是兩個(gè)獨(dú)立的類。
雙親委派模型
站在JVM的角度,只存在兩種不同的類加載器:
一種是啟動(dòng)類加載器(Bootstrap ClassLoader),是由C++實(shí)現(xiàn),是JVM的一部分。
另外一種是所有其他的類加載器,由Java實(shí)現(xiàn),獨(dú)立于JVM,并且都繼承自java.lang.ClassLoader.站在開發(fā)人員的角度,可以分為三類:
啟動(dòng)類加載器
擴(kuò)展類加載器
應(yīng)用程序類加載器
雙親委派模型:除了頂層的啟動(dòng)類加載器外,其余的類加載器都有自己的父類加載器。這里的父子關(guān)系不是通過繼承實(shí)現(xiàn),而是采用組合的方式復(fù)用父加載器的代碼。
雙親委派模型工作過程
- 如果一個(gè)類加載器收到了類加載請(qǐng)求。
- 它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器完成。
- 每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到頂層的啟動(dòng)類加載器中。
- 只有當(dāng)父加載器無法完成這個(gè)加載請(qǐng)求(它的搜索范圍內(nèi)沒有找到所需的類)時(shí),子加載器才會(huì)嘗試自己去加載。
雙親委派模型的特點(diǎn)
- Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。
- 如果沒有使用雙親委派模型,由各個(gè)類加載器自行去加載的話,有可能會(huì)出現(xiàn)由多個(gè)加載器加載的不同的同名類。
雙親委派模型實(shí)現(xiàn)代碼集中在java.lang.ClassLoader的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;
}
}
loadClass()方法執(zhí)行邏輯:
- 首先查找是否已經(jīng)被加載。
- 若沒有被加載過,則調(diào)用父加載器的loadClass()方法。
- 若父加載器為空,則默認(rèn)使用啟動(dòng)類加載器作為父加載器進(jìn)行加載。
- 如果上述加載均失敗,則拋出ClassNotFoundException異常,再調(diào)用自己的findClass()方法進(jìn)行加載。