回覆列表
  • 1 # 程式猿W
    Java裡有如下幾種類載入器引導類載入器:負責載入支撐JVM執行的位於JRE的lib目錄下的核心類庫,比如rt.jar、charsets.jar等。擴充套件類載入器:負責載入支撐JVM執行的位於JRE的lib目錄下的ext擴充套件目錄中的JAR類包應用程式類載入器:負責載入ClassPath路徑下的類包,主要就是載入你自己寫的那些類自定義載入器:負責載入使用者自定義路徑下的類包類載入器初始化過程

    參見類執行載入全過程圖可知其中會建立JVM啟動器例項sun.misc.Launcher。

    sun.misc.Launcher初始化使用了單例模式設計,保證一個JVM虛擬機器內只有一個sun.misc.Launcher例項

    在Launcher構造方法內部,其建立了兩個類載入器,分別是sun.misc.Launcher.ExtClassLoader(擴充套件類載入器)和sun.misc.Launcher.AppClassLoader(應用類載入器)

    JVM預設使用Launcher的 getClassLoader()方法返回的類載入器 AppClassLoader的例項載入我們的應用程式

    類的主動使用和被動使用

    1.主動使用

    透過new關鍵字導致類的初始化:這種是大家經常採用的初始化一個類的方式,它肯定會導致類的載入並且最終初始化。訪問類的靜態變數,包括讀取 和 更新會導致類的初始化訪問類的靜態方法,會導致類的初始化對某個類進行反射操作,會導致類的初始化初始化子類會導致父類的初始化啟動類: 也就是執行main函式所在的類會導致該類的初始化

    2、除了上面6中,其餘都稱為被動使用,不會導致類的載入 和初始化

    構造某個類的陣列時並不會導致該類的初始化

    Simple[] simples = new Simple[10];

    上面的程式碼中new方法新建了一個Simple型別的陣列,但是它並不能導致Simple類的初始化,因此它是被動使用,不要被前面的new關鍵字鎖誤導,事實上該操作只不過是在堆記憶體中開闢了一段連續的地址空間

    引用靜態常量不會導致類的初始化

    (1) public fianl static int MAX = 10

    在其他類中使用MAX不會導致類的初始化,靜態程式碼塊不會輸出

    (2) public static final int RANDOM = new Random().nextInt()

    雖然RANDOM是靜態常量,但是由於計算複雜,只有初始化之後才能得到結果,因此在其他類中使用RANDOM會導致類的初始化

    因為RANDOM需要進行隨機函式計算的,在類的載入、連線階段是無法對其進行計算的,需要進行初始化後才能對其富裕準確的值。

    雙親委派機制

    執行結果:

    類的雙親委派載入機制如下:

    這裡類載入其實就有一個雙親委派機制,載入某個類時會先委託父載入器尋找目標類,找不到再委託上層父載入器載入,如果所有父載入器在自己的載入類路徑下都找不到目標類,則在自己的類載入路徑中查詢並載入目標類比如我們的Math類,最先會找應用程式類載入器載入,應用程式類載入器會先委託擴充套件類載入器載入,擴充套件類載入器再委託引導類載入器,頂層引導類載入器在自己的類載入路徑裡找了半天沒找到ath類,則向下退回載入Math類的請求,擴充套件類載入器收到回覆就自己載入,在自己的類載入路徑找了半天也沒找到Math類,又向下退回Math類的載入請求給應用程式類載入器,應用程式類載入器於是在自己的類載入路徑裡找Math類,結果找到了就自己載入了。。雙親委派機制說簡單點就是,先找父親載入,不行再由兒子自己載入我們來看下應用程式類載入器 AppClassLoader 載入類的雙親委派機制原始碼,AppClassLoader 的loadClass方法最終會呼叫其父類 ClassLoader 的 loadClass 方法,該方法的大體邏輯如下

    1. 首先,檢查一下指定名稱的類是否已經載入過,如果載入過了,就不需要再載入,直接返回。

    2. 如果此類沒有載入過,那麼,再判斷一下是否有父載入器;如果有父載入器,則由父載入器載入(即呼叫 parent.loadClass(name, false);).或者是呼叫 bootstrap 類載入器來載入。

    3. 如果父載入器及 bootstrap 類載入器都沒有找到指定的類,那麼呼叫當前類載入器的 findClass 方法來完成類載入。

    類與類載入器 :類載入器 + 類 = 唯一

    為什麼要設計雙親委派機制?

    (1)沙箱安全機制:自己寫的java.lang.String.class類不會被載入,這樣便可以防止核心API庫被隨意篡改。

    (2)避免類的重複載入:當父親已經載入了該類時,就沒有必要子ClassLoader再載入一次,保證被載入類的唯一性。

    自定義類載入器示例

    自定義類載入器只需要繼承java.lang.ClassLoader類,該類有兩個核心方法,一個是loadClass(String,boolean),實現了雙親委派機制,還有一個方法是findClass,預設實現的是空方法,所以我們自定義類載入器主要是重新findClass方法。

    Tomcat打破雙親委派機制

    1、Tomcat是一個web容器,那麼它要解決什麼問題呢?

    1. 一個web容器可能需要部署兩個應用程式,不同的應用程式可能會依賴同一個第三方類庫的不同版本,不能要求同一個類庫在同一個伺服器只有一份,因此要保證每個應用程式的類庫都是獨立的,保證相互隔離

    2. 部署在同一個web容器中相同的類庫相同的版本可以共享。否則,如果伺服器有10個應用程式,那麼要有10份相同的類庫載入進虛擬機器。

    3. web容器也有自己依賴的類庫,不能與應用程式的類庫混淆。基於安全考慮,應該讓容器的類庫和程式的類庫隔離開來。

    4. web容器要支援jsp的修改,我們知道,jsp 檔案最終也是要編譯成class檔案才能在虛擬機器中執行,但程式執行後修改jsp已經是司空見慣的事情, web容器需要支援 jsp 修改後不用重啟。

    2.Tomcat 如果使用預設的雙親委派類載入機制行不行?

    答案是不行的。為什麼?

    第一個問題,如果使用預設的類載入器機制,那麼是無法載入兩個相同類庫的不同版本的,預設的類加器是不管你是什麼版本的,只在乎你的全限定類名,並且只有一份。第二個問題,預設的類載入器是能夠實現的,因為他的職責就是保證唯一性。第三個問題,我們想我們要怎麼實現jsp檔案的熱載入,jsp 檔案其實也就是class檔案,那麼如果修改了,但類名還是一樣,類載入器會直接取方法區中已經存在的,修改後的jsp是不會重新載入的。那麼怎麼辦呢?每個jsp檔案對應一個唯一的類載入器,當一個jsp檔案修改了,就直接解除安裝這個jsp類載入器。重新建立類載入器,重新載入jsp檔案。

    3.Tomcat自定義載入器詳解

    CommonClassLoader能載入的類都可以被CatalinaClassLoader和SharedClassLoader使用,從而實現了公有類庫的共用,而CatalinaClassLoader和SharedClassLoader自己能載入的類則與對方相互隔離。WebAppClassLoader可以使用SharedClassLoader載入到的類,但各個WebAppClassLoader例項之間相互隔離而JasperLoader的載入範圍僅僅是這個JSP檔案所編譯出來的那一個.Class檔案,它出現的目的就是為了被丟棄:當Web容器檢測到JSP檔案被修改時,會替換掉目前的JasperLoader的例項,並透過再建立一個新的Jsp類載入器來實現JSP檔案的熱載入功能。

    tomcat的幾個主要類載入器

    commonLoader:Tomcat最基本的類載入器,載入路徑中的class可以被Tomcat容器本身以及各個Webapp訪問;catalinaLoader:Tomcat容器私有的類載入器,載入路徑中的class對於Webapp不可見sharedLoader:各個Webapp共享的類載入器,載入路徑中的class對於所有Webapp可見,但是對於Tomcat容器不可見;WebappClassLoader:各個Webapp私有的類載入器,載入路徑中的class只對當前Webapp可見,比如載入war包裡相關的類,每個war包應用都有自己的,WebappClassLoader,實現相互隔離,比如不同war包應用引入了不同的sprig版本,這樣實現就能載入各自的spring版本

    tomcat 這種類載入機制違背了java 推薦的雙親委派模型了嗎?答案是:違背了。很顯然,tomat 不是這樣實現,tomcat 為了實現隔離性,沒有遵守這個約定,每個webappClassLoader載入自己的目錄下的class檔案,不會傳遞給父類載入器,打破了雙親委派機制

  • 2 # 此生唯一

    我們都知道jvm類載入使用的是雙親委派模型,那麼到底什麼是雙親委派模型?這樣做的好處又是什麼呢?又如何打破雙親委派模型呢?

    先來了解下JVM中的類載入器:JVM類載入器大致可以分為以下幾種:

    1,啟動類載入器:Bootstrap ClassLoader:負責載入jre\lib下面的jar中的類進入記憶體,在HotSpot虛擬機器中,是使用C++進行實現;

    2,擴充套件類載入器:Extension ClassLoader:負責載入jre\lib\ext下面的擴充套件型jar中的類進行入記憶體;

    3,系統類載入器:Application ClassLoader:負責載入應用中classpath目錄下面的所有jar。

    4,自定義載入器:透過繼承ClassLoader,可實現自己的類載入器,通常可以用在熱部署,網路輸入的流等類庫;

    幾種類載入器的繼承關係如下:

    雙親委派模型:一個類載入器收到類載入任務的時候,會先檢視是不是已經被載入過,如果沒有載入過則委託給父載入器進行載入,父載入器經過同樣的過程,如果父載入器搜尋不到這個類,則讓子載入器自己嘗試著載入,如果所有載入器都找不到,則丟擲ClassNotFoundException異常;

    一句話總結雙親委派模型的特點是:父親能幹的事就讓父親幹,幹不了再讓兒子來;

    雙親委派模型好處:比如說,某“壞人”在將jre\lib下面的包中的某個類(a.b.c)中,意圖植入大量的惡意程式碼,但因為這個原本的類已經被Bootstrap ClassLoader載入過了,後面加入的a.b.c不會在載入進入記憶體中,雙親委派模型的類載入方式,就能防止惡意程式碼的向頂層汙染;

    打破雙親委派模型:要打破雙親委派模型的載入方式,首先需要整合ClassLoader,然後再重寫loadClass(),findClass(),因為雙親委派的程式碼邏輯主要在loadClass方法中,重寫覆蓋loadClass方法,讓父類載入器載入失敗,這樣就可以交由自定義的類載入器載入了;

    雙親委派模型就說這麼多,下次將會從ClassLoader類載入器的原始碼角度,詳細分析下類載入器的載入過程,如果有需要的朋友,敬請關注。。

  • 中秋節和大豐收的關聯?
  • 電氣裝置從生產到執行會存在哪些隱患?如何判斷是否可以繼續使用?