为何采用双亲委派机制
一、雙親(父親)委派機制
java中存在3種類型的類加載器:引導類加載器,擴展類加載器和系統類加載器。三者是的關系是:引導類加載器是擴展類加載器的父類,擴展類加載器是系統類加載器的父類。
- 引導類加載器(BootStrap)
主要負責加載jvm自身所需要的類,該加載器由C++實現,加載的是<JAVA_HOME>/lib下的class文件,或-Xbootclasspath參數指定的路徑下的jar包加載到內存中,注意必由于虛擬機是按照文件名識別加載jar包的,如rt.jar,如果文件名不被虛擬機識別,即使把jar包丟到lib目錄下也是沒有作用的(出于安全考慮,Bootstrap啟動類加載器只加載包名為java、javax、sun等開頭的類)。 - 拓展類加載器(Extension)
擴展類加載器是指Sun公司(已被Oracle收購)實現的sun.misc.Launcher$ExtClassLoader類,由Java語言實現的,是Launcher的靜態內部類,它負責加載<JAVA_HOME>/lib/ext目錄下或者由系統變量-Djava.ext.dir指定位路徑中的類庫,開發者可以直接使用標準擴展類加載器。
- 系統類加載器
也稱應用程序加載器是指 Sun公司實現的sun.misc.Launcher$AppClassLoader。它負責加載系統類路徑java -classpath或-D java.class.path指定路徑下的類庫,也就是我們經常用到的classpath路徑,開發者可以直接使用系統類加載器,一般情況下該類加載是程序中默認的類加載器,通過ClassLoade.getSystemClassLoader()方法可以獲取到該類加載器。
在Java的日常應用程序開發中,類的加載幾乎是由上述3種類加載器相互配合執行的,在必要時,我們還可以自定義類加載器,需要注意的是,Java虛擬機對class文件采用的是按需加載的方式,也就是說當需要使用該類時才會將它的class文件加載到內存生成class對象,而且加載某個類的class文件時,Java虛擬機采用的是雙親委派模式即把請求交由父類處理,它一種任務委派模式,下面我們進一步了解它。
二、目的
- 都是用同名的類完成實例化的。
- 兩個實例各自對應的同名的類的加載器必須是同一個。比如兩個相同名字的類,一個是用系統加載器加載的,一個擴展類加載器加載的,兩個類生成的對象將被jvm認定為不同類型的對象。
面試題:
能不能自己寫個類叫java.lang.System?
答案:通常不可以,但可以采取另類方法達到這個需求。
解釋:為了不讓我們寫System類,類加載采用委托機制,這樣可以保證爸爸們優先,爸爸們能找到的類,兒子就沒有機會加載。而System類是Bootstrap加載器加載的,就算自己重寫,也總是使用Java系統提供的System,自己寫的System類根本沒有機會得到加載。
但是,我們可以自己定義一個類加載器來達到這個目的,為了避免雙親委托機制,這個類加載器也必須是特殊的。由于系統自帶的三個類加載器都加載特定目錄下的類,如果我們自己的類加載器加載一個特殊的目錄,那么系統的加載器就無法加載,也就是最終還是由我們自己的加載器加載。
------------------------------------------------------------------------------------------------------------------------------------------------------
什么是雙親委派機制
當某個類加載器需要加載某個.class文件時,它首先把這個任務委托給他的上級類加載器,遞歸這個操作,如果上級的類加載器沒有加載,自己才會去加載這個類。
類加載器的類別
BootstrapClassLoader(啟動類加載器)
c++編寫,加載java核心庫 java.*,構造ExtClassLoader和AppClassLoader。由于引導類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啟動類加載器的引用,所以不允許直接通過引用進行操作
ExtClassLoader (標準擴展類加載器)
java編寫,加載擴展庫,如classpath中的jre ,javax.*或者
java.ext.dir 指定位置中的類,開發者可以直接使用標準擴展類加載器。
AppClassLoader(系統類加載器)
java編寫,加載程序所在的目錄,如user.dir所在的位置的class
CustomClassLoader(用戶自定義類加載器)
java編寫,用戶自定義的類加載器,可加載指定路徑的class文件
源碼分析
?
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 首先檢查這個classsh是否已經加載過了Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {// c==null表示沒有加載,如果有父類的加載器則讓父類加載器加載if (parent != null) {c = parent.loadClass(name, false);} else {//如果父類的加載器為空 則說明遞歸到bootStrapClassloader了//bootStrapClassloader比較特殊無法通過get獲取c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {}if (c == null) {//如果bootstrapClassLoader 仍然沒有加載過,則遞歸回來,嘗試自己去加載classlong t1 = System.nanoTime();c = findClass(name);sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}委派機制的流程圖
image.png
雙親委派機制的作用
1、防止重復加載同一個.class。通過委托去向上面問一問,加載過了,就不用再加載一遍。保證數據安全。
2、保證核心.class不能被篡改。通過委托方式,不會去篡改核心.clas,即使篡改也不會去加載,即使加載也不會是同一個.class對象了。不同的加載器加載同一個.class也不是同一個Class對象。這樣保證了Class執行安全。
作者:秦時的明月夜
鏈接:https://www.jianshu.com/p/1e4011617650
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的为何采用双亲委派机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 函数式接口@FunctionalInte
- 下一篇: 图解Tomcat类加载机制(阿里面试题)