ClassLoader原理

ClassLoader原理

Classloader體系結構

    系統自帶的類加載器主要有:Bootstrap class loader、Extensions class loader、System class loader。html

  1. Bootstrap class loader 負責加載」 <JAVA_HOME>/jre/lib」目錄的JAR包,如rt.jar等。該加載器使用C語言實現,不是Java代碼實現的。
  2. Extensions class loader 負責加載」 <JAVA_HOME>/jre/lib/ext」目錄的Jar,這個加載器的是Java語言實現的,sun.misc.Launcher$ExtClassLoader,是個內部類。
  3. System class loader負責加載」 java.class.path」目錄類,這個類也是Java實現的,sun.misc.Launcher$AppClassLoader。

驗證類加載器的層次結構:java

    從上面能夠看到HashMap是Null(由於不是Java語言實現,因此是Null),便是Bootstrap class loader加載,DNSNameService由Extensions class loader加載,當前類ClassLoaderTest由System class loader(AppClassLoader)加載。安全

類加載模型

    類加載是指Class文件加載到JVM併產生Class對象的遠程。類加載,其實包括類和接口的加載,如下咱們統一稱爲類加載。類加載使用雙親委派模型,即類加載器把要加載的類委派給其父加載器,父加載器再把要加載類委派給其父加載器,直到Bootstrap class loader爲止。被委派的類加載器就造成了委派鏈,其中Bootstrap class loader稱委派鏈的未端。若是要加載的類不在被委派的加載器職責範圍,被委派的加載器返回null給其子一級加載器,由子一級的加載器加載,若是因此委派鏈都不加載,則由當前類加載器加載。spa

    若是被加載的類是類A,屬於用戶自定義類並在」 java.class.path」路徑下,假設當前類加載器是System class loader。那加載的流程是這樣的:System class loader把類A委派給Extensions class loader,而後Extensions class loader又把類A委派給Bootstrap class loader,而後Bootstrap class loader發現類A不屬於Bootstrap class loader加載範圍內,返回Null給Extensions class loader, Extensions class loader也發現類A也不屬於其加範圍內,返回Null給System class loader,那System class loader嘗試加載類A,獲得類A的Class對象。orm

    若是被加載的類是類DNSNameService,屬於dnsns.jar的<JAVA_HOME>/jre/lib/ext路徑下,假設當前類加載器是System class loader。那加載的流程是這樣的:System class loader把類DNSNameService委派給Extensions class loader,而後Extensions class loader又把類DNSNameService委派給Bootstrap class loader(注意,由於Extensions class loader不是委派鏈的未端,因此必須進行下一步委派),而後Bootstrap class loader發現類A不屬於Bootstrap class loader加載範圍內,返回Null給Extensions class loader, Extensions class loader也發現類DNSNameService屬於其加範圍內,加載DNSNameService,並返回DNSNameService的Class對換給System class loader,System class loader就能夠直接使用DNSNameService的類了。htm

    若是被加載的類是類HashMap,屬於dnsns.jar的<JAVA_HOME>/jre/lib/ext路徑下,假設當前類加載器是System class loader。那加載的流程是這樣的:System class loader把類HashMap委派給Extensions class loader,而後Extensions class loader又把類HashMap委派給Bootstrap class loader,而後Bootstrap class loader發現類HashMap屬於Bootstrap class loader加載範圍內(目錄<JAVA_HOME>/jre/lib的rt.jar內),由Bootstrap class loader加載HashMap,並獲得HashMap的Class對象,並把HashMap的Class對象返回給Extensions class loader,再由Extensions class loader返回給System class loader。對象

    異常狀況,好比:黑客也寫了一個帶惡意HashMap的類,而且徹底限定名也是java.util.HashMap。若是system class loader要加載這個帶惡意的HashMap,先委派給Extensions class loader,而後再委派給Bootstrap class loader。 因爲Bootstrap class loader負責的目錄」 <JAVA_HOME>/jre/lib 」 下已存在相同的徹底限定名的HashMap,因而Bootstrap class loader只加載」 <JAVA_HOME>/jre/lib 」目錄下HashMap類,並建立Class對象,並返回給Extensions class loader,而後由Extensions class loader返回 System class loader。這樣System class loader獲得是一個安全的HashMap,而帶惡意的HashMap則沒有被丟棄了,沒有被加載,避免了被入侵的危險。dns

    能夠看到出委派模型的優勢是安全,避免JRE自帶的內置的類被外來的類覆蓋。從上圖能夠看出,委派模型決定加載器優先級,優先級最高是Bootstrap class loader,其次是Extensions class loader,而後是System class loader,最後纔是用戶自定義的類。接口

類加載器的命名空間

    每一個類加載器都擁用本身的命名空間,只有在同一個命名空間的類才能互相引用和轉換(Cast),即同一個命名空間下的類才互相可見。其實,JVM的方法區內,會爲每一個類加載器維護一個表,表記錄當前加載器的所加載的類。父加載器的命名空間的類對子加載器是可見的,就是說子加載器可見類範圍是最大的,全部父 (包括多級父類)加載器加載的類對子類是可見的。因此子類的加載器的類能夠引用父加載器的類,反之不成立。兄弟類加載器的命名空間之間的類是不可見的。好比:ClassLoaderA和ClassLoaderB均爲System class loader的子加載器,ClassLoaderA加載類A,ClassLoaderB加載類B,此時類A和類B是不可見的,即不能互相引用,不然會報異常。這種錯誤很是隱蔽,很難發現,特別是當系統存在多個加載器時,特別要謹慎。ssl

關聯類加載

    JVM默認當前使用類的加載器去加載被引用的類。假設A類的加載器爲:System class loader,好比:A類(使用類)引用了B類(引用類),那JVM默認使用System class loader加載B類,固然,加載類B時一樣會使用雙親委派的模型。你們所熟悉的Class.forName方法,也是使用這個規則去加載類。在使用Class.forName時會使用當前類的加載器去加載目標類。

類加載器的選擇

    估計你們接觸得最可能是context classLoader、System class loader、Class.forName三種方式,看完下面這兩篇文章就明白了吧,我就不囉嗦了。

https://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html?page=1

https://stackoverflow.com/questions/1771679/difference-between-threads-context-class-loader-and-normal-classloader

相關文章
相關標籤/搜索