JDK9引入了Java模塊化系統(Java Platform Moudle System)來實現可配置的封裝隔離機制,同時JVM對類加載的架構也作出了調整,也就是雙親委派模型的第四次破壞。前三次破壞分別是:雙親委派模型推出以前,SPI機制,以及OSGI爲表明的熱替換機制,這裏不細說。java
在JDK9引入以前,絕大多數Java程序會用下面三個類加載器進行加載數據結構
若是一個ClassLoader收到了類加載的請求,他會先首先將請求委派給父類加載器完成,只有父類加載器加載不了,子加載器纔會完成加載。架構
<img src="https://tva1.sinaimg.cn/large/00831rSTly1gdatbhj3zsj30hs0b1mxc.jpg" alt="雙親委派模型" style="zoom:70%;" />併發
下面代碼保留了核心邏輯,並添加了註釋,主要是2個步驟app
Class<?> c = findLoadedClass(name); //若是該類沒加載過 if (c == null) { try { //若是有父類加載器 if (parent != null) { //使用父類加載器加載 c = parent.loadClass(name, false); ... } } if (c == null) { ... //父類加載器沒有加載成功則調用自身的findClass進行加載 c = findClass(name); ... } }
值得注意的是這裏的parent並非繼承上的父子關係,而是組合關係的父子,parent只是類加載器的一個參數。maven
若是以爲上面的解釋比較抽象能夠看看下面比較形象的圖示,這裏的敵人就是咱們要加載的jar包模塊化
<img src="https://tva1.sinaimg.cn/large/00831rSTly1gdat1wvelvj30uc0e4n5c.jpg" alt="工做順序" style="zoom:57%;" />源碼分析
經過上面的漫畫不言而喻,當真正的敵人來了,靠這種低效的傳達機制,怎麼可能打一場勝仗呢?ui
既然一切都是各司其職,爲何不能加載類的時候一步到位呢?code
經過分析JDK9的類加載器源碼,我發現最新的類加載器結構在必定程度上是緩解了這種狀況的
在JDK9以前,JVM的基礎類之前都是在rt.jar這個包裏,這個包也是JRE運行的基石。這不只是違反了單一職責原則,一樣程序在編譯的時候會將不少無用的類也一併打包,形成臃腫。
在JDK9中,整個JDK都基於模塊化進行構建,之前的rt.jar, tool.jar被拆分紅數十個模塊,編譯的時候只編譯實際用到的模塊,同時各個類加載器各司其職,只加載本身負責的模塊。
<img src="https://tva1.sinaimg.cn/large/00831rSTly1gdaucc9rlyj30ro0vcgrv.jpg" alt="moudle" style="zoom:40%;" />
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); } }
上面代碼就是破壞雙親委派模型的「鐵證」,而當咱們繼續跟進findLoadedModule,會發現是根據路徑名找到對應的模塊,而維護這一數據結構的就是下面這個Map。
Map<String, LoadedModule> packageToModule = new ConcurrentHashMap<>(1024);
能夠看到LoadedModule裏面不只有該模塊的loader信息,還有用於描述依賴模塊,對外暴露模塊的信息的mref,LoadedModule也是模塊化實現封裝隔離機制的一塊重要實現。
<img src="https://tva1.sinaimg.cn/large/00831rSTly1gdauzxorg6j31240pwqa7.jpg" alt="moduleMap" style="zoom:47%;" />
每個module信息都有一個BuiltinClassloader,這個類有三個子類,咱們經過源碼分析他們的父子關係
<img src="https://tva1.sinaimg.cn/large/00831rSTly1gdavhdpd40j30x408s40s.jpg" alt="image-20200329162147803" style="zoom:47%;" />
在ClassLoaders類中能夠發現,PlatformClassLoader的parent是BootClassLoader,而AppClassLoader的parent則是PlatformClassLoader。
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); } }