JVM自帶加載器
-
啟動(dòng)類(lèi)加載器 BootStrap ClassLoader:最頂層的類(lèi)加載器,負(fù)責(zé)加載 JAVA_HOME\lib 目錄中的稻爬,或通過(guò)-Xbootclasspath參數(shù)指定路徑中的蒿秦,且被虛擬機(jī)認(rèn)可(按文件名識(shí)別座享,如rt.jar)的類(lèi)菱涤〕裾ぃ可以通
System.getProperty("sun.boot.class.path")
查看加載的路徑海诲。 -
擴(kuò)展類(lèi)加載器 Extention ClassLoader:主要加載目錄
%JRE_HOME%\lib\ext
目錄下的jar
包和class
文件繁莹,或通過(guò)java.ext.dirs系統(tǒng)變量指定路徑中的類(lèi)庫(kù)。也可以通過(guò)System.out.println(System.getProperty("java.ext.dirs"))
查看加載類(lèi)文件的路徑特幔。 -
應(yīng)用程序類(lèi)加載器 Application ClassLoader:也叫做系統(tǒng)類(lèi)加載器咨演,可以通過(guò)
getSystemClassLoader()
獲取,負(fù)責(zé)加載用戶(hù)路徑classpath
上的類(lèi)庫(kù)蚯斯。如果沒(méi)有自定義類(lèi)加載器薄风,一般這個(gè)就是默認(rèn)的類(lèi)加載器饵较。
類(lèi)加載層次關(guān)系
類(lèi)加載器之間的這種層次關(guān)系叫做雙親委派模型。
雙親委派模型要求除了頂層的啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader)外遭赂,其余的類(lèi)加載器都應(yīng)當(dāng)有自己的父類(lèi)加載器循诉。這里的類(lèi)加載器之間的父子關(guān)系一般不是以繼承關(guān)系實(shí)現(xiàn)的,而是用組合實(shí)現(xiàn)的撇他。
- 下面看一段源碼
public class Launcher {
private static Launcher launcher = new Launcher();
private static String bootClassPath =
System.getProperty("sun.boot.class.path");
public static Launcher getLauncher() {
return launcher;
}
private ClassLoader loader;
public Launcher() {
// Create the extension class loader
ClassLoader extcl;
try {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
"Could not create extension class loader", e);
}
// Now create the class loader to use to launch the application
try {
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader", e);
}
Thread.currentThread().setContextClassLoader(loader);
}
/*
* Returns the class loader used to launch the main application.
*/
public ClassLoader getClassLoader() {
return loader;
}
/*
* The class loader used for loading installed extensions.
*/
static class ExtClassLoader extends URLClassLoader {}
/**
* The class loader used for loading from java.class.path.
* runs in a restricted security context.
*/
static class AppClassLoader extends URLClassLoader {}
從源碼中我們看到
(1)Launcher
初始化的時(shí)候創(chuàng)建了ExtClassLoader
以及AppClassLoader
茄猫,并將ExtClassLoader
實(shí)例傳入到AppClassLoader
中。
(2)雖然上一段源碼中沒(méi)見(jiàn)到創(chuàng)建BoopStrap ClassLoader
困肩,但是程序一開(kāi)始就執(zhí)行了System.getProperty("sun.boot.class.path")
划纽。
附上Launcher
相關(guān)文章:
https://blog.csdn.net/jyxmust/article/details/72357372?utm_source=itdadao&utm_medium=referral
- 類(lèi)加載器中的繼承關(guān)系
AppClassLoader
的父加載器為ExtClassLoader
,ExtClassLoader
的父加載器為null
锌畸,BoopStrap ClassLoader
為頂級(jí)加載器勇劣。
類(lèi)加載機(jī)制-雙親委托
當(dāng)JVM加載Test.class
類(lèi)的時(shí)候
- 首先會(huì)到自定義加載器中查找,看是否已經(jīng)加載過(guò)蹋绽,如果已經(jīng)加載過(guò)芭毙,則返回該類(lèi)。
- 如果自定義加載器沒(méi)有加載過(guò)卸耘,則詢(xún)問(wèn)上一層加載器(即
AppClassLoader
)是否已經(jīng)加載過(guò)Test.class
退敦。 - 如果沒(méi)有加載過(guò),則詢(xún)問(wèn)上一層加載器(
ExtClassLoader
)是否已經(jīng)加載過(guò)蚣抗。 - 如果沒(méi)有加載過(guò)侈百,則繼續(xù)詢(xún)問(wèn)上一層加載(
BoopStrap ClassLoader
)是否已經(jīng)加載過(guò)。 - 如果
BoopStrap ClassLoader
沒(méi)有加載過(guò)翰铡,則到自己指定類(lèi)加載路徑sun.boot.class.path
下查看是否有Test.class
字節(jié)碼钝域,有則加載并返回加載后的類(lèi)c = findBootstrapClassOrNull(name)
。 - 如果還是沒(méi)找到調(diào)用
c = findClass(name)
到加載器ExtClassLoader
指定的類(lèi)加載路徑java.ext.dirs
下查找class
文件锭魔,有則加載并返回類(lèi)例证。 - 依此類(lèi)推,最后到自定義類(lèi)加載器指定的路徑還沒(méi)有找到
Test.class
字節(jié)碼迷捧,則拋出異常ClassNotFoundException
织咧。
這里注意
每個(gè)自定義的類(lèi)加載器都需要重寫(xiě)findClass
方法,該方法的作用是到指定位置查找class
文件并加載到JVM中漠秋,如果找不到則拋出ClassNotFoundException
異常笙蒙。
雙親委派模型最大的好處就是讓Java類(lèi)同其類(lèi)加載器一起具備了一種帶優(yōu)先級(jí)的層次關(guān)系。這句話(huà)可能不好理解庆锦,我們舉個(gè)例子捅位。比如我們要加載頂層的Java類(lèi)——
java.lang.Object
類(lèi),無(wú)論我們用哪個(gè)類(lèi)加載器去加載Object
類(lèi),這個(gè)加載請(qǐng)求最終都會(huì)委托給Bootstrap ClassLoader
艇搀,這樣就保證了所有加載器加載的Object
類(lèi)都是同一個(gè)類(lèi)尿扯。
雙親委派模型的實(shí)現(xiàn)比較簡(jiǎ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;
}
}
/**
* Finds the class with the specified <a href="#name">binary name</a>.
* This method should be overridden by class loader implementations that
* follow the delegation model for loading classes, and will be invoked by
* the {@link #loadClass <tt>loadClass</tt>} method after checking the
* parent class loader for the requested class. The default implementation
* throws a <tt>ClassNotFoundException</tt>.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
參考鏈接:
http://www.reibang.com/p/5f79217f2e18
https://nomico271.github.io/2017/07/07/JVM%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/
https://www.cnblogs.com/gdpuzxs/p/7044963.html