前言
JDK9引入了Java模塊化系統(Java Platform Moudle System)來實現可配置的封裝隔離機制,同時JVM對類加載的架構也做出了調整,也就是雙親委派模型的第四次破壞。前三次破壞分別是:雙親委派模型推出之前,SPI機制,以及OSGI為代表的熱替換機制,這裡不細說。
雙親委派模型
簡介
在JDK9引入之前,絕大多數Java程序會用下面三個類加載器進行加載
啟動類加載器(Bootstrap Class Loader):由C++編寫,負責加載<java>\\jre\\lib目錄下的類,例如最基本的Object,Integer,這些存在於rt.jar文件中的類,一般這些類都是Java程序的基石。/<java>擴展類加載器(Extension Class Loader):負責加載<java>\\jre\\lib\\ext目錄下的類,在JDK9之前我們可以將通用性的類庫放在ext目錄來擴展JAVA的功能,但實際的工程都是通過maven引入jar包依賴。並且在JDK9取消了這一類加載器,取而代之的是平臺類加載器(Platform Class Loader),下面會對其介紹。/<java>應用類加載器(Application Class Loader):負責加載ClassPath路徑下的類,通常工程師編寫的大部分類都是由這個類加載器加載。工作順序
解釋
如果一個ClassLoader收到了類加載的請求,他會先首先將請求委派給父類加載器完成,只有父類加載器加載不了,子加載器才會完成加載。
源代碼
下面代碼保留了核心邏輯,並添加了註釋,主要是2個步驟
<code> Class> c = findLoadedClass(name);
//如果該類沒加載過
if (c == null) {
try {
//如果有父類加載器
if (parent != null) {
//使用父類加載器加載
c = parent.loadClass(name, false);
...
}
}
if (c == null) {
...
//父類加載器沒有加載成功則調用自身的findClass進行加載
c = findClass(name);
\t\t\t\t\t\t\t\t\t\t...
}
}
/<code>
值得注意的是這裡的parent並不是繼承上的父子關係,而是組合關係的父子,parent只是類加載器的一個參數。
圖示
如果覺得上面的解釋比較抽象可以看看下面比較形象的圖示,這裡的敵人就是我們要加載的jar包
缺點
通過上面的漫畫不言而喻,當真正的敵人來了,靠這種低效的傳達機制,怎麼可能打一場勝仗呢?
啟動類加載器負責加載<java>\\jre\\lib目錄/<java>擴展類加載器負責加載<java>\\jre\\lib\\ext目錄/<java>應用類加載器負責加載ClassPath目錄。既然一切都是各司其職,為什麼不能加載類的時候一步到位呢?
通過分析JDK9的類加載器源碼,我發現最新的類加載器結構在一定程度上是緩解了這種情況的
JDK的模塊化
在JDK9之前,JVM的基礎類以前都是在rt.jar這個包裡,這個包也是JRE運行的基石。這不僅是違反了單一職責原則,同樣程序在編譯的時候會將很多無用的類也一併打包,造成臃腫。
在JDK9中,整個JDK都基於模塊化進行構建,以前的rt.jar, tool.jar被拆分成數十個模塊,編譯的時候只編譯實際用到的模塊,同時各個類加載器各司其職,只加載自己負責的模塊。
模塊化加載源碼
<code> Class> c = findLoadedClass(cn);
if (c == null) {
// 找到當前類屬於哪個模塊
LoadedModule loadedModule = findLoadedModule(cn);
if (loadedModule != null) {
//獲取當前模塊的類加載器
BuiltinClassLoader loader = loadedModule.loader();
//進行類加載
c = findClassInModuleOrNull(loadedModule, cn);
} else {
// 找不到模塊信息才會進行雙親委派
if (parent != null) {
c = parent.loadClassOrNull(cn);
}
}/<code>
上面代碼就是破壞雙親委派模型的“鐵證”,而當我們繼續跟進findLoadedModule,會發現是根據路徑名找到對應的模塊,而維護這一數據結構的就是下面這個Map。
<code>Map<string> packageToModule
= new ConcurrentHashMap<>(1024);/<string>/<code>
可以看到LoadedModule裡面不僅有該模塊的loader信息,還有用於描述依賴模塊,對外暴露模塊的信息的mref,LoadedModule也是模塊化實現封裝隔離機制的一塊重要實現。
每一個module信息都有一個BuiltinClassloader,這個類有三個子類,我們通過源碼分析他們的父子關係
在ClassLoaders類中可以發現,PlatformClassLoader的parent是BootClassLoader,而AppClassLoader的parent則是PlatformClassLoader。
<code>public class ClassLoaders {
// the built-in class loaders
private static final BootClassLoader BOOT_LOADER;
private static final PlatformClassLoader PLATFORM_LOADER;
private static final AppClassLoader APP_LOADER;
static {
BOOT_LOADER =
new BootClassLoader((append != null && !append.isEmpty())
? new URLClassPath(append, true)
: null);
PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
...
APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
}
}/<code>