5、JVM — 類加載器

  • 回顧一下類加載過程
  • 類加載器總結
  • 雙親委派模型
    • 雙親委派模型介紹
    • 雙親委派模型實現源碼分析
    • 雙親委派模型的好處
    • 若是咱們不想要雙親委派模型怎麼辦?
  • 自定義類加載器
  • 推薦

回顧一下類加載過程

類加載過程:加載->鏈接->初始化。鏈接過程又可分爲三步:驗證->準備->解析。java

類加載過程

一個非數組類的加載階段(加載階段獲取類的二進制字節流的動做)是可控性最強的階段,這一步咱們能夠去完成還能夠自定義類加載器去控制字節流的獲取方式(重寫一個類加載器的 loadClass() 方法)。數組類型不經過類加載器建立,它由 Java 虛擬機直接建立。git

全部的類都由類加載器加載,加載的做用就是將 .class文件加載到內存。github

類加載器總結

JVM 中內置了三個重要的 ClassLoader,除了 BootstrapClassLoader 其餘類加載器均由 Java 實現且所有繼承自java.lang.ClassLoader數組

  1. BootstrapClassLoader(啓動類加載器) :最頂層的加載類,由C++實現,負責加載 %JAVA_HOME%/lib目錄下的jar包和類或者或被 -Xbootclasspath參數指定的路徑中的全部類。
  2. ExtensionClassLoader(擴展類加載器) :主要負責加載目錄 %JRE_HOME%/lib/ext 目錄下的jar包和類,或被 java.ext.dirs 系統變量所指定的路徑下的jar包。
  3. AppClassLoader(應用程序類加載器) :面向咱們用戶的加載器,負責加載當前應用classpath下的全部jar包和類。

雙親委派模型

雙親委派模型介紹

每個類都有一個對應它的類加載器。系統中的 ClassLoder 在協同工做的時候會默認使用 雙親委派模型 。即在類加載的時候,系統會首先判斷當前類是否被加載過。已經被加載的類會直接返回,不然纔會嘗試加載。**加載的時候,首先會把該請求委派該父類加載器的 loadClass() 處理,所以全部的請求最終都應該傳送到頂層的啓動類加載器 BootstrapClassLoader中。當父類加載器沒法處理時,才由本身來處理。**當父類加載器爲null時,會使用啓動類加載器 BootstrapClassLoader 做爲父類加載器。jvm

ClassLoader

每一個類加載都有一個父類加載器,咱們經過下面的程序來驗證。ide

public class ClassLoaderDemo { public static void main(String[] args) { System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader()); System.out.println("The Parent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent()); System.out.println("The GrandParent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent().getParent()); } }

Output源碼分析

ClassLodarDemo's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
The Parent of ClassLodarDemo's ClassLoader is sun.misc.Launcher$ExtClassLoader@1b6d3586
The GrandParent of ClassLodarDemo's ClassLoader is null

AppClassLoader的父類加載器爲ExtClassLoader ExtClassLoader的父類加載器爲null,null並不表明ExtClassLoader沒有父類加載器,而是 Bootstrap ClassLoader 。post

其實這個雙親翻譯的容易讓別人誤解,咱們通常理解的雙親都是父母,這裏的雙親更多地表達的是「父母這一輩」的人而已,並非說真的有一個 Mather ClassLoader 和一個 Father ClassLoader 。另外,類加載器之間的「父子」關係也不是經過繼承來體現的,是由「優先級」來決定。官方API文檔對這部分的描述以下:ui

The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a "parent" class loader. When loading a class, a class loader first "delegates" the search for the class to its parent class loader before attempting to find the class itself.this

雙親委派模型實現源碼分析

雙親委派模型的實現代碼很是簡單,邏輯很是清晰,都集中在 java.lang.ClassLoader 的 loadClass() 中,相關代碼以下所示。

private final ClassLoader parent; protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先,檢查請求的類是否已經被加載過 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) {//父加載器不爲空,調用父加載器loadClass()方法處理 c = parent.loadClass(name, false); } else {//父加載器爲空,使用啓動類加載器 BootstrapClassLoader 加載 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { //拋出異常說明父類加載器沒法完成加載請求 } if (c == null) { long t1 = System.nanoTime(); //本身嘗試加載 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }

雙親委派模型的好處

雙親委派模型保證了Java程序的穩定運行,能夠避免類的重複加載(JVM 區分不一樣類的方式不單單根據類名,相同的類文件被不一樣的類加載器加載產生的是兩個不一樣的類),也保證了 Java 的核心 API 不被篡改。若是不用沒有使用雙親委派模型,而是每一個類加載器加載本身的話就會出現一些問題,好比咱們編寫一個稱爲 java.lang.Object 類的話,那麼程序運行的時候,系統就會出現多個不一樣的 Object 類。

若是咱們不想用雙親委派模型怎麼辦?

爲了不雙親委託機制,咱們能夠本身定義一個類加載器,而後重載 loadClass() 便可。

自定義類加載器

除了 BootstrapClassLoader 其餘類加載器均由 Java 實現且所有繼承自java.lang.ClassLoader。若是咱們要自定義本身的類加載器,很明顯須要繼承 ClassLoader

推薦閱讀

相關文章
相關標籤/搜索