談談 Java 類加載機制

概述

類加載器主要分爲兩類,一類是 JDK 默認提供的,一類是用戶自定義的。 JDK 默認提供三種類加載器:html

  1. Bootstrap ClassLoader 啓動類加載器:每次執行 java 命令時都會使用該加載器爲虛擬機加載核心類。該加載器是由 native code 實現,而不是 Java 代碼,加載類的路徑爲 <JAVA_HOME>/jre/lib。特別的 <JAVA_HOME>/jre/lib/rt.jar 中包含了 sun.misc.Launcher 類, 而 sun.misc.Launcher$ExtClassLoader 和 sun.misc.Launcher$AppClassLoader 都是 sun.misc.Launcher 的內部類,因此拓展類加載器和系統類加載器都是由啓動類加載器加載的。
  2. Extension ClassLoader, 拓展類加載器:用於加載拓展庫中的類。拓展庫路徑爲 <JAVA_HOME>/jre/lib/ext/。實現類爲 sun.misc.Launcher$ExtClassLoader
  3. System ClassLoader 系統類加載器:用於加載 CLASSPATH 中的類。實現類爲 sun.misc.Launcher$AppClassLoader

歡迎學Java和大數據的朋友們加入java架構交流: 855835163
羣內提供免費的架構資料還有:Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點高級進階乾貨的免費直播講解  能夠進來一塊兒學習交流哦
java

用戶自定義的類加載器

  1. Custom ClassLoader, 通常都是 java.lang.ClassLoder 的子類

正統的類加載機制是基於雙親委派的,也就是當調用類加載器加載類時,首先將加載任務委派給雙親,若雙親沒法加載成功時,本身才進行類加載。web

在實例化一個新的類加載器時,咱們能夠爲其指定一個 parent,即雙親,若未顯式指定,則 System ClassLoader 就做爲默認雙親。apache

具體的說,類加載任務是由 ClassLoader 的 loadClass() 方法來執行的,他會按照如下順序加載類:bootstrap

  1. 經過 findLoadedClass() 看該類是否已經被加載。該方法爲 native code 實現,若已加載則返回。
  2. 若未加載則委派給雙親,parent.loadClass(),若成功則返回。
  3. 若未成功,則調用 findClass() 方法加載類。java.lang.ClassLoader 中該方法只是簡單的拋出一個 ClassNotFoundException 因此,自定義的 ClassLoader 都須要 Override findClass() 方法。

類加載API

java.lang.ClassLoader

  • ClassLoader 是一個抽象類。
  • 待加載的類必須用 The Java™ Language Specification 定義的全類名,全類名的定義請查閱 The Form of a Binary
  • 給定一個全類名,類加載器應該去定位該類所在的位置。通用的策略是將全類名轉換爲類文件路徑,而後經過類文件路徑在文件系統中定位。
  • 每個加載到內存的類都由一個 Class 對象來表示,每個 Class 對象都有一個指向加載該類的類加載器的引用。可是數組的 Class 對象是由 Java 運行時環境建立的,經過 Class.getClassLoader() 方法返回的是數組元素的類加載器,若數組元素是基本類型,則返回 null,若類是由 Bootstrap ClassLoader 加載的話也是返回 null。

    1數組

    2tomcat

    3安全

    4服務器

    5架構

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    public class Main {

        public static void main(String[] args) {

            // Object 類在 <java_home>/jre/lib/rt.jar 中,

            // 由 Bootstrap ClassLoader 加載,因爲該類加載器是由 native code 編寫

            // 因此輸出爲 null

            Object[] objects = new Object[5];

            System.out.println();

            System.out.println(objects.getClass().getClassLoader());

     

            // ZipFileAttributes 類在 <java_home>/jre/lib/ext/zipfs.jar 中,

            // 由 Extension ClassLoader 加載,

            // 輸出爲  sun.misc.Launcher$ExtClassLoader@4b67cf4d

            ZipFileAttributes[] attributes = new ZipFileAttributes[5];

            System.out.println();

            System.out.println(attributes.getClass().getClassLoader());

     

            // Main 類是自定義的類,

            // 默認由 System ClassLoader 加載,

            // 輸出爲 sun.misc.Launcher$AppClassLoader@18b4aac2

            Main[] array = new Main[5];

            array[0] = new Main();

            System.out.println();

            System.out.println(array.getClass().getClassLoader());

        }

    }

  • ClassLoader 默認支持並行加載,可是其子類必須調用 ClassLoader.registerAsParallelCapable() 來啓用並行加載
  • 通常來講,JVM 從本地文件系統加載類的行爲是與平臺有關的。
  • defineClass() 方法能夠將字節流轉換成一個 Class 對象。而後調用 Class.newInstance() 來建立類的實例

java.security.SecureClassLoader

增長了一層權限驗證,由於關注點不在安全,因此暫不討論。

java.net.URLClassLoader

該類加載器用來加載 URL 指定的 JAR 文件或目錄中的類和資源,以 / 結尾的 URL 認爲是目錄,不然認爲是 JAR 文件。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

// 嘗試經過 URLClassLoader 來加載桌面下的 Test 類。

public class Main {

    public static void main(String[] args) {

        try {

            URL[] urls = new URL[1];

            URLStreamHandler streamHandler = null;

            File classPath = new File("/home/chen/Desktop/");

            String repository = (new URL("file", null,

                    classPath.getCanonicalPath() + File.separator))

                    .toString();

            urls[0] = new URL(null, repository, streamHandler);

 

            ClassLoader loader = new URLClassLoader(urls);

 

            Class testClass = loader.loadClass("Test");

 

            // output:  java.net.URLClassLoader@7f31245a

            System.out.println(testClass.getClassLoader());

        } catch (MalformedURLException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        } catch (ClassNotFoundException e) {

            e.printStackTrace();

        }

    }

}

Tomcat 8.5.15類加載機制

Tomcat 使用正統的類加載機制(雙親委派),但部分地方作了改動。

  • Bootstrap classLoader 和 Extension classLoader 的做用不變。
  • System classLoader 正常狀況下加載的是 CLASSPATH 下的類,可是 Tomcat 的啓動腳本並未使用該變量,而是從如下倉庫下加載類:
  1. $CATALINA_HOME/bin/bootstrap.jar 包含了 Tomcat 的啓動類。在該啓動類中建立了 Common classLoaderCatalina classLoadershared classLoader。由於 $CATALINA_BASE/conf/catalina.properties 中只對 common.loader 屬性作了定義,server.loader 和 shared.loader 屬性爲空,因此默認狀況下,這三個 classLoader 都是 CommonLoader。具體的代碼邏輯能夠查閱 org.apache.catalina.startup.Bootstrap 類的 initClassLoaders() 方法和 createClassLoader() 方法。
  2. $CATALINA_BASE/bin/tomcat-juli.jar 包含了 Tomcat 日誌模塊所須要的實現類。
  3. $CATALINA_HOME/bin/commons-daemon.jar。
  • Common classLoader 是位於 Tomcat 應用服務器頂層的公用類加載器。由其加載的類能夠由 Tomcat 自身類和全部應用程序使用。掃描路徑由 $CATALINA_BASE/conf/catalina.properties 文件中的 common.loader 屬性定義。默認是 $CATALINA_HOME/lib。
  • catalina classLoader 用於加載服務器內部可見類,這些類應用程序不能訪問。
  • shared classLoader 用於加載應用程序共享類,這些類服務器不會依賴。
  • Webapp classLoader 。每一個應用程序都會有一個獨一無二的 webapp classloader,他用來加載本應用程序 /WEB-INF/classes 和 /WEB-INF/lib 下的類。

特別的:

Webapp classLoader 的默認行爲會與正常的雙親委派模式不一樣:

  1. 從 Bootstrap classloader 加載。
  2. 若沒有,從 /WEB-INF/classes 加載。
  3. 若沒有,從 /WEB-INF/lib/*.jar 加載。
  4. 若沒有,則依次從 SystemCommonshared 加載(該步驟使用雙親委派)。

固然了,咱們也能夠經過配置來使 Webapp classLoader 嚴格按照雙親委派模式加載類:

  1. 經過在工程的 META-INF/context.xml(和 WEB-INF/classes 在同一目錄下) 配置文件中添加 <Loader delegate="true"/>
  2. 由於 Webapp classLoader 的實現類是 org.apache.catalina.loader.WebappLoader,他有一個屬性叫 delegate, 用來控制類加載器的加載行爲,默認爲 false,咱們可使用 set 方法,將其設爲 true 來啓用嚴格雙親委派加載模式。

嚴格雙親委派模式加載步驟:

  1. 從 Bootstrap classloader 加載。
  2. 若沒有,則依次從 SystemCommonshared 加載。
  3. 若沒有,從 /WEB-INF/classes 加載。
  4. 若沒有,從 /WEB-INF/lib/*.jar 加載。

歡迎學Java和大數據的朋友們加入java架構交流: 855835163 羣內提供免費的架構資料還有:Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點高級進階乾貨的免費直播講解  能夠進來一塊兒學習交流哦

相關文章
相關標籤/搜索