什么是jvm雙親委派模型?
謝謝邀請(qǐng)!我將從以下幾點(diǎn)進(jìn)行說明
Java里有如下幾種類加載器引導(dǎo)類加載器:負(fù)責(zé)加載支撐JVM運(yùn)行的位于JRE的lib目錄下的核心類庫,比如rt.jar、charsets.jar等。擴(kuò)展類加載器:負(fù)責(zé)加載支撐JVM運(yùn)行的位于JRE的lib目錄下的ext擴(kuò)展目錄中的JAR類包應(yīng)用程序類加載器:負(fù)責(zé)加載ClassPath路徑下的類包,主要就是加載你自己寫的那些類自定義加載器:負(fù)責(zé)加載用戶自定義路徑下的類包類加載器初始化過程參見類運(yùn)行加載全過程圖可知其中會(huì)創(chuàng)建JVM啟動(dòng)器實(shí)例sun.misc.Launcher。
sun.misc.Launcher初始化使用了單例模式設(shè)計(jì),保證一個(gè)JVM虛擬機(jī)內(nèi)只有一個(gè)sun.misc.Launcher實(shí)例
在Launcher構(gòu)造方法內(nèi)部,其創(chuàng)建了兩個(gè)類加載器,分別是sun.misc.Launcher.ExtClassLoader(擴(kuò)展類加載器)和sun.misc.Launcher.AppClassLoader(應(yīng)用類加載器)
JVM默認(rèn)使用Launcher的 getClassLoader()方法返回的類加載器 AppClassLoader的實(shí)例加載我們的應(yīng)用程序
類的主動(dòng)使用和被動(dòng)使用1.主動(dòng)使用
通過new關(guān)鍵字導(dǎo)致類的初始化:這種是大家經(jīng)常采用的初始化一個(gè)類的方式,它肯定會(huì)導(dǎo)致類的加載并且最終初始化。訪問類的靜態(tài)變量,包括讀取 和 更新會(huì)導(dǎo)致類的初始化訪問類的靜態(tài)方法,會(huì)導(dǎo)致類的初始化對(duì)某個(gè)類進(jìn)行反射操作,會(huì)導(dǎo)致類的初始化初始化子類會(huì)導(dǎo)致父類的初始化啟動(dòng)類: 也就是執(zhí)行main函數(shù)所在的類會(huì)導(dǎo)致該類的初始化2、除了上面6中,其余都稱為被動(dòng)使用,不會(huì)導(dǎo)致類的加載 和初始化
構(gòu)造某個(gè)類的數(shù)組時(shí)并不會(huì)導(dǎo)致該類的初始化Simple[] simples = new Simple[10];
上面的代碼中new方法新建了一個(gè)Simple類型的數(shù)組,但是它并不能導(dǎo)致Simple類的初始化,因此它是被動(dòng)使用,不要被前面的new關(guān)鍵字鎖誤導(dǎo),事實(shí)上該操作只不過是在堆內(nèi)存中開辟了一段連續(xù)的地址空間
引用靜態(tài)常量不會(huì)導(dǎo)致類的初始化(1) public fianl static int MAX = 10
在其他類中使用MAX不會(huì)導(dǎo)致類的初始化,靜態(tài)代碼塊不會(huì)輸出
(2) public static final int RANDOM = new Random().nextInt()
雖然RANDOM是靜態(tài)常量,但是由于計(jì)算復(fù)雜,只有初始化之后才能得到結(jié)果,因此在其他類中使用RANDOM會(huì)導(dǎo)致類的初始化
因?yàn)镽ANDOM需要進(jìn)行隨機(jī)函數(shù)計(jì)算的,在類的加載、連接階段是無法對(duì)其進(jìn)行計(jì)算的,需要進(jìn)行初始化后才能對(duì)其富裕準(zhǔn)確的值。
雙親委派機(jī)制運(yùn)行結(jié)果:
類的雙親委派加載機(jī)制如下:
這里類加載其實(shí)就有一個(gè)雙親委派機(jī)制,加載某個(gè)類時(shí)會(huì)先委托父加載器尋找目標(biāo)類,找不到再委托上層父加載器加載,如果所有父加載器在自己的加載類路徑下都找不到目標(biāo)類,則在自己的類加載路徑中查找并載入目標(biāo)類比如我們的Math類,最先會(huì)找應(yīng)用程序類加載器加載,應(yīng)用程序類加載器會(huì)先委托擴(kuò)展類加載器加載,擴(kuò)展類加載器再委托引導(dǎo)類加載器,頂層引導(dǎo)類加載器在自己的類加載路徑里找了半天沒找到ath類,則向下退回加載Math類的請(qǐng)求,擴(kuò)展類加載器收到回復(fù)就自己加載,在自己的類加載路徑找了半天也沒找到Math類,又向下退回Math類的加載請(qǐng)求給應(yīng)用程序類加載器,應(yīng)用程序類加載器于是在自己的類加載路徑里找Math類,結(jié)果找到了就自己加載了。。雙親委派機(jī)制說簡(jiǎn)單點(diǎn)就是,先找父親加載,不行再由兒子自己加載我們來看下應(yīng)用程序類加載器 AppClassLoader 加載類的雙親委派機(jī)制源碼,AppClassLoader 的loadClass方法最終會(huì)調(diào)用其父類 ClassLoader 的 loadClass 方法,該方法的大體邏輯如下1. 首先,檢查一下指定名稱的類是否已經(jīng)加載過,如果加載過了,就不需要再加載,直接返回。
2. 如果此類沒有加載過,那么,再判斷一下是否有父加載器;如果有父加載器,則由父加載器加載(即調(diào)用 parent.loadClass(name, false);).或者是調(diào)用 bootstrap 類加載器來加載。
3. 如果父加載器及 bootstrap 類加載器都沒有找到指定的類,那么調(diào)用當(dāng)前類加載器的 findClass 方法來完成類加載。
類與類加載器 :類加載器 + 類 = 唯一
為什么要設(shè)計(jì)雙親委派機(jī)制?(1)沙箱安全機(jī)制:自己寫的java.lang.String.class類不會(huì)被加載,這樣便可以防止核心API庫被隨意篡改。
(2)避免類的重復(fù)加載:當(dāng)父親已經(jīng)加載了該類時(shí),就沒有必要子ClassLoader再加載一次,保證被加載類的唯一性。
自定義類加載器示例自定義類加載器只需要繼承java.lang.ClassLoader類,該類有兩個(gè)核心方法,一個(gè)是loadClass(String,boolean),實(shí)現(xiàn)了雙親委派機(jī)制,還有一個(gè)方法是findClass,默認(rèn)實(shí)現(xiàn)的是空方法,所以我們自定義類加載器主要是重新findClass方法。
Tomcat打破雙親委派機(jī)制1、Tomcat是一個(gè)web容器,那么它要解決什么問題呢?
1. 一個(gè)web容器可能需要部署兩個(gè)應(yīng)用程序,不同的應(yīng)用程序可能會(huì)依賴同一個(gè)第三方類庫的不同版本,不能要求同一個(gè)類庫在同一個(gè)服務(wù)器只有一份,因此要保證每個(gè)應(yīng)用程序的類庫都是獨(dú)立的,保證相互隔離
2. 部署在同一個(gè)web容器中相同的類庫相同的版本可以共享。否則,如果服務(wù)器有10個(gè)應(yīng)用程序,那么要有10份相同的類庫加載進(jìn)虛擬機(jī)。
3. web容器也有自己依賴的類庫,不能與應(yīng)用程序的類庫混淆。基于安全考慮,應(yīng)該讓容器的類庫和程序的類庫隔離開來。
4. web容器要支持jsp的修改,我們知道,jsp 文件最終也是要編譯成class文件才能在虛擬機(jī)中運(yùn)行,但程序運(yùn)行后修改jsp已經(jīng)是司空見慣的事情, web容器需要支持 jsp 修改后不用重啟。
2.Tomcat 如果使用默認(rèn)的雙親委派類加載機(jī)制行不行?
答案是不行的。為什么?
第一個(gè)問題,如果使用默認(rèn)的類加載器機(jī)制,那么是無法加載兩個(gè)相同類庫的不同版本的,默認(rèn)的類加器是不管你是什么版本的,只在乎你的全限定類名,并且只有一份。第二個(gè)問題,默認(rèn)的類加載器是能夠?qū)崿F(xiàn)的,因?yàn)樗穆氊?zé)就是保證唯一性。第三個(gè)問題,我們想我們要怎么實(shí)現(xiàn)jsp文件的熱加載,jsp 文件其實(shí)也就是class文件,那么如果修改了,但類名還是一樣,類加載器會(huì)直接取方法區(qū)中已經(jīng)存在的,修改后的jsp是不會(huì)重新加載的。那么怎么辦呢?每個(gè)jsp文件對(duì)應(yīng)一個(gè)唯一的類加載器,當(dāng)一個(gè)jsp文件修改了,就直接卸載這個(gè)jsp類加載器。重新創(chuàng)建類加載器,重新加載jsp文件。3.Tomcat自定義加載器詳解
CommonClassLoader能加載的類都可以被CatalinaClassLoader和SharedClassLoader使用,從而實(shí)現(xiàn)了公有類庫的共用,而CatalinaClassLoader和SharedClassLoader自己能加載的類則與對(duì)方相互隔離。WebAppClassLoader可以使用SharedClassLoader加載到的類,但各個(gè)WebAppClassLoader實(shí)例之間相互隔離而JasperLoader的加載范圍僅僅是這個(gè)JSP文件所編譯出來的那一個(gè).Class文件,它出現(xiàn)的目的就是為了被丟棄:當(dāng)Web容器檢測(cè)到JSP文件被修改時(shí),會(huì)替換掉目前的JasperLoader的實(shí)例,并通過再建立一個(gè)新的Jsp類加載器來實(shí)現(xiàn)JSP文件的熱加載功能。tomcat的幾個(gè)主要類加載器
commonLoader:Tomcat最基本的類加載器,加載路徑中的class可以被Tomcat容器本身以及各個(gè)Webapp訪問;catalinaLoader:Tomcat容器私有的類加載器,加載路徑中的class對(duì)于Webapp不可見sharedLoader:各個(gè)Webapp共享的類加載器,加載路徑中的class對(duì)于所有Webapp可見,但是對(duì)于Tomcat容器不可見;WebappClassLoader:各個(gè)Webapp私有的類加載器,加載路徑中的class只對(duì)當(dāng)前Webapp可見,比如加載war包里相關(guān)的類,每個(gè)war包應(yīng)用都有自己的,WebappClassLoader,實(shí)現(xiàn)相互隔離,比如不同war包應(yīng)用引入了不同的sprig版本,這樣實(shí)現(xiàn)就能加載各自的spring版本tomcat 這種類加載機(jī)制違背了java 推薦的雙親委派模型了嗎?答案是:違背了。很顯然,tomat 不是這樣實(shí)現(xiàn),tomcat 為了實(shí)現(xiàn)隔離性,沒有遵守這個(gè)約定,每個(gè)webappClassLoader加載自己的目錄下的class文件,不會(huì)傳遞給父類加載器,打破了雙親委派機(jī)制
如果對(duì)你有幫助 ,歡迎關(guān)注@碼農(nóng)的一天