Java類加載器(死磕 1-2)

 

Java類加載器(  CLassLoader ) 死磕 1、2: 

導入 & 類加載器分類

本小節目錄


1.導入
1.1. 從class文件的載入開始
1.2. 什麼是類加載器
2. JAVA類加載器分類
2.1. 操作系統的環境變量
2.2. Bootstrap ClassLoader(啓動類加載器)
2.3. Extention ClassLoader (擴展類加載器)
2.4 Appclass Loader(應用類加載器)

2.5. 加載器啓動次序
2.6. 閱讀源碼,查看加載器啓動順序
2.7. 本節小結


1.導入

1.1.從class文件的載入開始


我們都知道在Java中程序是運行在虛擬機中,我們平常用文本編輯器或者是IDE編寫的程序都是.java格式的文件,這是最基礎的源碼,但這類文件是不能直接運行的。如我們編寫一個簡單的程序HelloWorld.java,Java虛擬機並不能直接識別我們平常編寫的.java源文件,所以需要javac這個命令轉換成.class文件,class文件是字節碼格式文件。
關鍵性的一步來了,.class如果加入JVM中呢?
這就需要用到Java的類加載器。


1.2.什麼是類加載器


ClassLoader翻譯過來就是類加載器,普通的java開發者其實用到的不多,但對於某些框架開發者來說卻非常常見。理解ClassLoader的加載機制,也有利於我們編寫出更高效的代碼。ClassLoader的具體作用就是將class文件加載到jvm虛擬機中去,程序就可以正確運行了。但是,jvm啓動的時候,並不會一次性加載所有的class文件,而是根據需要去動態加載。想想也是的,一次性加載那麼多jar包那麼多class,那內存不崩潰。本文的目的也是學習ClassLoader這種加載機制。


2.JAVA類加載器分類

講解類加載器,首先回顧一下系統的環境變量。


2.1.操作系統的環境變量


操作系統中,與java相關的重要的系統環境變量,你知道有哪些嗎?
初學java的時候,最害怕是配置環境變量了,關鍵是不理解,只能戰戰兢兢地照着書籍上或者是網絡上的介紹進行操作。太不照顧新手了,很多菜鳥就是因爲卡在環境變量的配置上,遭受了太多的挫敗感。 

java相關的環境變量的配置,主要有3個:
JAVA_HOME、PATH、CLASSPATH
具體的配置方式,與操作系統相關。 在Windows下編程時,可以通過系統屬性的環境變量界面,配置以上的三個環境變量。
下面介紹一下三個環境變量的含義。

一:JAVA_HOME

指的是你JDK安裝的位置,一般默認安裝在C盤,如:

C:\Program Files\Java\jdk1.8.0_131

二:PATH

PATH變量,是系統之前就已經一直存在和一直使用的。是系統指令和命令的搜索路徑。設置PATH變量的方法是:
PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%PATH%;

在原來的PATH路徑上添加JDK目錄下的bin目錄和jre目錄的bin。將Java的bin 路徑包含在PATH當中後,在命令行窗口就可以直接鍵入Java命令的名字了,而不再需要鍵入它的全路徑。比如編譯java源文件的javac命令,和執行字節碼的java命令。

如果沒有將Java的bin路徑加入到path中,執行javac命令的方法如下:

C:\Program Files\Java\jdk1.8.0_91\bin\javac  test.java


設置了設置PATH變量之後,執行javac命令的方法如下:

javac  test.java



三:CLASSPATH

CLASSPATH是指向jar包路徑和class類路徑。
一個配置的例子如下:

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

需要注意的是前面的. 點號,代表可以在當前目錄搜索java 類文件。

以上的三個Java 系統的環境變量,都與Java的類加載器密切相關。Java語言系統自帶有三個類加載器,具體如下圖:


ClassLoader 3


2.2.Bootstrap ClassLoader(啓動類加載器)


Bootstrap ClassLoader是最頂層加載器,主要加載核心類庫,%java.home%\lib下的rt.jar、charsets.jar和class等。


注意,這裏的 %java.home% 不是JAVA_HOME環境變量,但是與JAVA_HOME環境變量的值相關。有很多文章介紹到這裏的時候,將 %java.home% 與JAVA_HOME搞混淆了。
專門澄清一下:

%java.home%  指的是JAVA語言運行時的系統屬性java.home;JAVA_HOME環境變量,指的是JDK在操作系統中的路徑。

通過下面的語句可以獲得%java.home% 其值:

    private static void showjavahome()
  {
      String javahome = System.getProperty("java.home");
      Logger.info("javahome = " + javahome);
    }

返回的結果是:

javahome = C:\Program Files\Java\jdk1.8.0_131\jre

對比一下,而前一個小節,我們設置的JAVA_HOME環境變量的值爲:

C:\Program Files\Java\jdk1.8.0_131

所以,兩者是不一樣的。Bootstrap ClassLoader(啓動類加載器)加載的類列表清單,保存在系統屬性%sun.boot.class.path% 中,通過獲取系統屬性的值,可以查看。
代碼如下:

private static void showbootpath()
{
 String path = System.getProperty("sun.boot.class.path");
  Logger.info("sun.boot.class.path = " + path);
}

結果如下:

sun.boot.class.path =  C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\sunrsasign.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\classes

通過結果可以看出,系統屬性%sun.boot.class.path%中的jar包和類路徑,都處於系統屬性%java.home% 下。

另外需要注意的是:可以通過啓動jvm時指定一個參數選項,來改變Bootstrap ClassLoader的加載目錄。這個參數選項爲 -Xbootclasspath。
例如:


被指定的文件追加到默認的bootstrap路徑中。

Bootstrap ClassLoader是由C/C++編寫的,它本身是虛擬機的一部分,所以它並不是一個JAVA類,也就是無法在java代碼中獲取它的引用,JVM啓動時通過Bootstrap類加載器加載rt.jar等核心jar包中的class文件,比如int.class,String.class都是由它加載。

 

2.3.Extention ClassLoader (擴展類加載器)


Extention ClassLoader (擴展的類加載器),加載系統屬性%java.ext.dirs%目錄下的jar包和class文件。

Extention ClassLoader (擴展的類加載器)加載的類列表清單,保存在系統屬性%java.ext.dirs% 中,通過獲取該系統屬性的值,可以查看。
代碼如下:

  private static void showextpath()
     {
   String path = System.getProperty("java.ext.dirs");
  Logger.info("java.ext.dirs = " + path);
}

返回的結果如下:

java.ext.dirs =
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext;
C:\WINDOWS\Sun\Java\lib\ext

另外需要注意的是:還可以加載-D java.ext.dirs選項指定的目錄。


2.4. Appclass Loader(應用類加載器)


與前面兩個不同,Appclass Loader 是應用程序的類加載器。Bootstrap ClassLoader 和 Extention ClassLoader, 主要是用於虛擬機的系統類加載。

Appclass Loader(應用類加載器)的作用,加載當前應用類路徑classpath的所有類。

應用類加載器,也稱爲SystemAppClass (系統應用類加載器)。

Appclass Loader(應用類加載器)加載的類列表清單,保存在系統屬性%java.class.path % 中,通過獲取該系統屬性的值,可以查看。
代碼如下:

 private static void showclasspath()     {
   String path = System.getProperty("java.class.path");
    Logger.info("java.class.path = " + path);
  }

返回的結果如下:

回顧一下前面設置的操作系統下的環境變量CLASSPATH,如下:
CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

對比之後,發現有點不對。上面輸出的系統屬性%java.class.path%的值,與配置的CLASSPATH值不一致,原因是什麼呢?
因爲我們這裏使用了開發工具IntelliJ IDEA,在工程中重新配置了依賴包,相當於將系統屬性%java.class.path%的值,重新配置了一下。
具體如下圖:

ClassLoader 2

2.5.加載器啓動次序


前面介紹了的3個類加載器,通過啓動JVM啓動Java程序的時候,怎樣知道具體哪個先行啓動呢? 

這裏,可以先揭曉答案:

1.  Bootstrap CLassLoader  


2.  Extention ClassLoader  


3.  AppClassLoader


再囉嗦一句:BootstrapClassLoader、ExtClassLoader、AppClassLoader通過查閱相應的系統屬性sun.boot.class.path、java.ext.dirs和java.class.path來加載資源文件的。



2.6.閱讀源碼,查看加載器啓動順序


三大加載器的啓動順序,可以通過閱讀源碼來查看。

JVM在啓動是,會執行sun.misc.Launcher 類。在該類中,創建一個Extention ClassLoader 擴展類加載器,和一個應用類加載器AppClassLoader。
源碼如下:

public class Launcher {
    private static Launcher launcher = new Launcher();
    private static String bootClassPath =
        System.getProperty("sun.boot.class.path");

    private ClassLoader loader;

    public Launcher() {
        // 創建Extention ClassLoader 擴展類加載器
        ClassLoader extcl;
        try {
            extcl = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        // 創建應用類加載器AppClassLoader
        try {
            loader = Launcher.AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

   .....
}
}

在上面的代碼中,看到了擴展類加載器和應用類加載器的啓動,沒有看到Bootstrap Loader的啓動。
爲什麼呢?
從啓動次序來說,Bootstrap Loader作爲jvm核心的核心,加載位於%java.home% 清單中的JVM 的絕對核心和基礎的類,毫無疑問,絕對是首先啓動的。由於它是基礎加載器,大多數版本jvm的bootstrap loader都不是java實現,或者不是純java的實現。理論上,Bootstrap ClassLoader 不應該叫他是一個java類。因爲,它已經完全不用java實現了。Bootstrap ClassLoader是在jvm啓動時,就被構造起來的,負責java平臺核心庫。

所以,在sun.misc.Launcher 類中,看不到Bootstrap Loader的啓動。

完整的三大加載器的啓動順序如下:
1、開始執行java命令後,首先尋找jre目錄,尋找jvm.dll,並初始化JVM;
2、產生一個Bootstrap Loader(啓動類加載器);
3、創建Extended Loader(標準擴展類加載器);
4、創建AppClass Loader(系統類加載器),並將其父加載器(parent loader)設爲Extended Loader。

這裏講到了父加載器,這是一種加載器之間的重要的關係——加載器之間的層次關係。

JVM初始化sun.misc.Launcher並創建Extension ClassLoader和AppClassLoader實例。並將ExtClassLoader設置爲AppClassLoader的父加載器。


Bootstrap沒有父加載器,而且它是祖宗,它是作爲所有父加載器爲空的加載器的父加載器。

呵呵,捋一捋,這句話是不是很繞。

後文中,將對這種層次關係,展開進行詳細解讀。


2.7.本節小結

上面簡單介紹了3個ClassLoader。說明了它們加載的路徑。

並且還提到了-Xbootclasspath和-D java.ext.dirs這兩個虛擬機參數選項。 

上面也介紹了三大加載器之間的啓動順序。爲後面介紹加載器之間的層次關係,打下了一個鋪墊。


本節的代碼的類爲:SysPropertiesShow.java,

所在的路徑爲:com.crazymakercircle.classLoaderDemo.base 



源碼:


代碼工程:  classLoaderDemo.zip

下載地址:在瘋狂創客圈QQ羣文件共享。


瘋狂創客圈:如果說Java是一個武林,這裏的聚集一羣武癡, 交流編程體驗心得
QQ羣鏈接:
瘋狂創客圈QQ羣


無編程不創客,無案例不學習。 一定記得去跑一跑案例哦


類加載器 死磕 系列

1.導入

2. JAVA類加載器分類

3. 揭祕ClassLoader抽象基類

4. 神祕的雙親委託機制

5. 入門案例:自定義一個文件系統的自定義classLoader

6. 基礎案例:自定義一個網絡類加載器

7. 中級案例:設計一個加密的自定義網絡加載器

8. 高級案例1:使用ASM技術,結合類加載器,解密AOP原理

9. 高級案例2:上下文加載器原理和案例



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章