JVM类加载器
2019獨角獸企業重金招聘Python工程師標準>>>
虛擬機設計團隊把類加載階段中“通過一個類的全限定名來獲取描述此類的二進制字節流”這個動作放到Java虛擬機外部去實現,以便讓應用程序自己決定如何去獲取所需要的類。實現這個動作的模塊稱為“類加載器”。
對于任意一個類,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性,每一個類加載器,都有一個獨立的類名稱空間。也就是說,比較兩個類是否“相等”,只有在這兩個類是由同一個類加載器加載的前提下才有意義,否則即使兩個類來自同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不同,那這兩個類就一定不相等。
從Java虛擬機的角度講,只存在兩種不同的類加載器:
(1)?啟動類加載器(Bootstrap ClassLoader),用C++語言實現,是虛擬機自身的一部分;
(2)?所有其他的類加載器,由Java語言實現,獨立于虛擬機外部,并且都繼承自抽象類java.lang.ClassLoader。
從Java開發人員的角度來看,大多可以分為3種系統提供的類加載器:
(1)?啟動類加載器(Bootstrap ClassLoader):
這個類加載器負責將存放在<JAVA_HOME>\lib目錄中,或者被-Xbootclasspath虛擬機參數指定的路徑中,并且是虛擬機識別的類庫加載到虛擬機內存中。(僅按照文件名識別,如rt.jar,名稱不符合的類庫即使放在lib目錄中也不會被加載。)
(2)?擴展類加載器(Extension ClassLoader):
這個類加載器由sun.misc.Launcher&ExtClassLoader實現,它負責加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴展類加載器。
(3)?應用程序類加載器(Application ClassLoader):
這個類加載器由sun.misc.Launcher$ApplicationClassLoader實現。由于這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱為系統類加載器。它負責加載用戶類路徑(ClassPath)上所指定的類庫,開發者可以直接使用這個類加載器,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。
雙親委派模型
上圖所展示的這種層次關系就叫做類加載器的雙親委派模型。它要求除了頂層的啟動類加載器外,其余的類加載器都應當有自己的父類加載器。這里的父子關系不以繼承關系來實現,而是用組合關系來復用父加載器的代碼。它不是一個強制性的約束模型,而是Java設計者推薦給開發者的一種類加載實現方式。
雙親委派模型的工作過程:
如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該首先傳送到頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求時,子加載器才會嘗試自己去加載。
雙親委派模型的好處:
Java類隨著它的類加載器一起具備了一種帶有優先級的層次關系。如java.lang.Object,它存放在rt.jar中,無論哪一個類加載器要加載這個類,最終都是委派給處于模型最頂端的啟動類加載器進行加載,因此Object類在程序的各種類加載器環境中都是同一個類。
破壞雙親委派模型的3種情況:
1.?第一次破壞
由于雙親委派模型是在JDK1.2之后才被引入的,而類加載器和抽象類java.lang.ClassLoader則在JDK1.0時代就已經存在,面對已經存在的用戶自定義類加載器的實現代碼,Java設計者引入雙親委派模型時不得不做出一些妥協。在此之前,用戶去繼承java.lang.ClassLoader的唯一目的就是為了重寫loadClass()方法,因為虛擬機在進行類加載的時候會調用加載器的私有方法loadClassInternal(),而這個方法唯一邏輯就是去調用自己的loadClass()。雙親委派模型的具體邏輯就實現在loadClass()方法中,如果重寫該方法則意味著破壞了雙親委派模型。
2.?第二次破壞
雙親委派模型的第二次“被破壞”是由這個模型自身的缺陷所導致的,雙親委派很好地解決了各個類加載器的基礎類的同一問題(越基礎的類由越上層的加載器進行加載),基礎類之所以稱為“基礎”,是因為它們總是作為被用戶代碼調用的API,但世事往往沒有絕對的完美,有時基礎類又要調用回用戶的代碼。
比如JNDI服務,它的代碼由啟動類加載器去加載,但它需要調用由獨立廠商(用戶)實現的JNDI接口提供者(SPI,Service Provider Interface)的代碼,但啟動類加載器不可能認識這些代碼,為此Java設計團隊引入了線程上下文類加載器,JNDI服務使用這個類加載器去加載所需要的SPI代碼,也就是父類加載器請求子類加載器去完成類加載動作,打破了雙親委派模型的層次結構,逆向使用了類加載器。
3.?第三次破壞
雙親委派模型的第三次“被破壞”是由于用戶對程序動態性的追求導致的,這里所說的“動態性”指的是當前一些非常“熱門”的名詞:代碼熱替換、模塊熱部署等,簡單說就是機器不用重啟,只要部署上就能用。
如OSGi實現模塊化熱部署的關鍵是它自定義的類加載器機制的實現,每一個程序模塊(OSGi中稱為Bundle)都有一個子集的類加載器,當需要更換一個Bundle時,就把Bundle連同類加載器一起換掉以實現代碼的熱替換。在OSGi環境下,類加載器不再是雙親委派模型中的樹狀結構,而是更復雜的網狀結構,當收到類加載請求時,OSGi將按照下面的順序進行類搜索:
1)將java.*開頭的類委派給父類加載器加載。
2)否則,將委派列表名單內的類委派給父類加載器加載。
3)否則,將Import列表中的類委派給Export這個類的Bundle的類加載器加載。
4)否則,查找當前Bundle的ClassPath,使用自己的類加載器加載。
5)否則,查找類是否在自己的Fragment Bundle中,如果在,則委派給Fragment Bundle的類加載器加載。
6)否則,查找Dynamic Import列表的Bundle,委派給對應Bundle的類加載器加載。
7)否則,類加載器失敗。
上述查找順序中只有開頭兩點仍然符合雙親委派規則,其余都是在平級的類加載器中進行,破壞了雙親委派規則。
?
?
轉載于:https://my.oschina.net/edwardge/blog/1805799
總結
- 上一篇: linux有哪些桌面系统(Linux系统
- 下一篇: 纪录片《这里是香港》今日开播:感受传统和