文章目錄
1. 類加載器的源碼實現
Java 類加載機制與對象實例化 已經闡明瞭 Java 中類加載器的種類,簡單來說 Java 自帶有三個類加載器,它們的具體作用就是根據需要去動態地將 class 文件加載到 JVM 虛擬機中去。這三個類加載器對應的實現如下:
- 啓動類加載器,
Bootstrap ClassLoader
加載系統屬性sun.boot.class.path
指定的路徑下的類,它由C/C++編寫,是虛擬機的一部分,無法在 java 代碼中獲知其實現- 擴展類加載器,
Extension ClassLoader
加載系統屬性java.ext.dirs
指定的路徑下的類,實現類爲sun.misc.Launcher.ExtClassLoader
- 應用程序類加載器,
Application ClassLoader
加載環境變量ClassPath
中的類庫,也就是系統屬性java.class.path
指定的路徑,實現類爲sun.misc.Launcher.AppClassLoader
1.1 抽象的類加載器 ClassLoader
java.lang.ClassLoader
是 Java 層面對類加載器的抽象,這個類規範了類加載的基本流程,其中比較重要的屬性及方法如下:
parent
:父類加載器的引用,一個類加載器通常都會保存一個父類加載器的引用,用於實現雙親委派機制loadClass()
方法,該方法爲類加載器的核心方法,其中實現了雙親委派的邏輯
public abstract class ClassLoader {
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
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;
}
}
1.2 擴展類加載器 ExtClassLoader
sun.misc.Launcher.ExtClassLoader
是擴展類加載器的實現類,該類比較重要的方法如下:
getExtDirs()
方法中指定了其加載系統屬性java.ext.dirs
指定的路徑下的類- 其帶參構造方法調用父類構造方法,傳入的父類加載器參數明確指定爲 null,也就是擴展類加載器沒有 Java 層面的父類加載器
static class ExtClassLoader extends URLClassLoader {
private static volatile Launcher.ExtClassLoader instance;
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
if (instance == null) {
Class var0 = Launcher.ExtClassLoader.class;
synchronized(Launcher.ExtClassLoader.class) {
if (instance == null) {
instance = createExtClassLoader();
}
}
}
return instance;
}
private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
try {
return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
public Launcher.ExtClassLoader run() throws IOException {
File[] var1 = Launcher.ExtClassLoader.getExtDirs();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
MetaIndex.registerDirectory(var1[var3]);
}
return new Launcher.ExtClassLoader(var1);
}
});
} catch (PrivilegedActionException var1) {
throw (IOException)var1.getException();
}
}
void addExtURL(URL var1) {
super.addURL(var1);
}
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
private static File[] getExtDirs() {
String var0 = System.getProperty("java.ext.dirs");
File[] var1;
if (var0 != null) {
StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
int var3 = var2.countTokens();
var1 = new File[var3];
for(int var4 = 0; var4 < var3; ++var4) {
var1[var4] = new File(var2.nextToken());
}
} else {
var1 = new File[0];
}
return var1;
}
......
}
1.3 應用程序類加載器 AppClassLoader
應用程序類加載器也叫系統類加載器,sun.misc.Launcher.AppClassLoader
是其實現類,該類比較重要的方法如下:
getAppClassLoader()
方法指定了其加載系統屬性java.class.path
指定的路徑下的類- 重寫的
loadClass()
方法做了加載 class 文件的一個優化,如果某個類存在於緩存目錄但是又不在內存中,則直接由其進行連接
,否則還是需要交給父類加載器去加載
static class AppClassLoader extends URLClassLoader {
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
AppClassLoader(URL[] var1, ClassLoader var2) {
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
if (this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
......
}
2. 雙親委託機制的實現
上圖是一個可能的類加載流程,流程中不少重要的步驟是 native
方法實現,在 Java 層無法得知細節。Java 層加載類的核心方法代碼如下,簡單描述的話主要是以下幾個步驟:
- 一個
AppClassLoader
加載類時,首先查看該類是否已經被加載過,有則從緩存中獲取,否則需要委託給父類加載器去處理ExtClassLoader
處理流程和AppClassLoader
完全一致,如果它也沒有加載過目標類,則由Bootstrap ClassLoader
去執行加載邏輯,也就是在sun.mic.boot.class
配置的路徑去目標類加載,加載完成就返回,沒有加載到就讓子加載器自己去找Bootstrap ClassLoader
如果沒有成功加載到目標類,則ExtClassLoader
自己在java.ext.dirs
配置的路徑中去查找目標類加載,成功就返回,不成功再向下讓子加載器去加載類ExtClassLoader
加載類不成功,AppClassLoader
在java.class.path
配置的路徑下查找目標類,找到就加載返回。如果沒有找到接着讓子類加載器找,如果沒有子類就會拋出各種異常
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); // 1.首先判斷類是否已經被加載
if (c == null) { // 2. 未被加載則進行雙親委派加載
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) { //3.父類加載器 loadClass 未加載到類才調用 findClass
// 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;
}
}