談談雙親委派模型的第四次破壞-模塊化

前言

JDK9引入了Java模塊化系統(Java Platform Moudle System)來實現可配置的封裝隔離機制,同時JVM對類加載的架構也作出了調整,也就是雙親委派模型的第四次破壞。前三次破壞分別是:雙親委派模型推出以前,SPI機制,以及OSGI爲表明的熱替換機制,這裏不細說。java

雙親委派模型

簡介

在JDK9引入以前,絕大多數Java程序會用下面三個類加載器進行加載數據結構

  • 啓動類加載器(Bootstrap Class Loader):由C++編寫,負責加載<JAVA_HOME>jrelib目錄下的類,例如最基本的Object,Integer,這些存在於rt.jar文件中的類,通常這些類都是Java程序的基石。
  • 擴展類加載器(Extension Class Loader):負責加載<JAVA_HOME>jrelibext目錄下的類,在JDK9以前咱們能夠將通用性的類庫放在ext目錄來擴展JAVA的功能,但實際的工程都是經過maven引入jar包依賴。而且在JDK9取消了這一類加載器,取而代之的是平臺類加載器(Platform Class Loader),下面會對其介紹。
  • 應用類加載器(Application Class Loader):負責加載ClassPath路徑下的類,一般工程師編寫的大部分類都是由這個類加載器加載。

工做順序

解釋

若是一個ClassLoader收到了類加載的請求,他會先首先將請求委派給父類加載器完成,只有父類加載器加載不了,子加載器纔會完成加載。架構

<img src="https://tva1.sinaimg.cn/large/00831rSTly1gdatbhj3zsj30hs0b1mxc.jpg" alt="雙親委派模型" style="zoom:70%;" />併發

源代碼

下面代碼保留了核心邏輯,並添加了註釋,主要是2個步驟app

  1. 若是父類加載器不爲空則用父類加載器加載
  2. 父類加載器加載不成功則自己再加載
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

  • 啓動類加載器負責加載<JAVA_HOME>jrelib目錄
  • 擴展類加載器負責加載<JAVA_HOME>jrelibext目錄
  • 應用類加載器負責加載ClassPath目錄。

既然一切都是各司其職,爲何不能加載類的時候一步到位呢?code

經過分析JDK9的類加載器源碼,我發現最新的類加載器結構在必定程度上是緩解了這種狀況的

JDK的模塊化

在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);
    }
  }

結論

  1. 通過破壞後的雙親委派模型更加高效,減小了不少類加載器之間沒必要要的委派操做
  2. JDK9的模塊化能夠減小Java程序打包的體積,同時擁有更好的隔離線與封裝性
  3. 每一個moudle擁有專屬的類加載器,程序在併發性上也會更加出色
相關文章
相關標籤/搜索