Java虛擬機類加載機制(三)——類加載器

類加載器:實現 “ 通過類的全限定名來獲取描述此類的二進制字節流 ” 的模塊
在這裏插入圖片描述

類加載器種類:

  • 啓動類加載器:負責加載支撐JVM運行的位於jre/lib目錄下的核心類庫(例如:String、Object類),在虛擬機啓動時就會加載完,以支撐虛擬機的運行。對於hotspot,這個類加載器使用C++實現。
  • 擴展類加載器:負責加載支撐JVM運行的位於jre/lib/ext中的JAR包。由Java語言實現,父類加載器爲null。
  • 應用程序類加載器:負責加載用戶路徑ClassPath下的類庫。由Java語言實現,父類加載器爲ExtClassLoader。

類加載器加載Class大致要經過如下8個步驟:

  1. 檢測此Class是否載入過,即在緩衝區中是否有此Class,如果有直接進入第8步,否則進入第2步。
  2. 如果沒有父類加載器,則要麼Parent是根類加載器,要麼本身就是根類加載器,則跳到第4步,如果父類加載器存在,則進入第3步。
  3. 請求使用父類加載器去載入目標類,如果載入成功則跳至第8步,否則接着執行第5步。
  4. 請求使用根類加載器去載入目標類,如果載入成功則跳至第8步,否則跳至第7步。
  5. 當前類加載器嘗試尋找Class文件,如果找到則執行第6步,如果找不到則執行第7步。
  6. 從文件中載入Class,成功後跳至第8步。
  7. 拋出ClassNotFountException異常。
  8. 返回對應的java.lang.Class對象。

類加載機制:

  • 全盤負責:所謂全盤負責,就是當一個類加載器負責加載某個Class時,該Class所依賴和引用其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入。
  • 雙親委派:所謂的雙親委派,則是先讓父類加載器試圖加載該Class,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父加載器,依次遞歸,如果父加載器可以完成類加載任務,就成功返回;只有父加載器無法完成此加載任務時,才自己去加載。
  • 緩存機制。緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區中搜尋該Class,只有當緩存區中不存在該Class對象時,系統纔會讀取該類對應的二進制數據,並將其轉換成Class對象,存入緩衝區中。這就是爲很麼修改了Class後,必須重新啓動JVM,程序所做的修改纔會生效的原因。

雙親委派機制
在這裏插入圖片描述
雙親委派機制要求除了頂層的啓動類加載器外,其餘的類加載器都應當有自己的父類加載器,但這裏的父子關係是組合關係

組合關係,是關聯關係的一種,是比聚合關係強的關係。它要求普通的聚合關係中代表整體的對象負責代表部分對象的生命週期,組合關係是不能共享的。代表整體的對象需要負責保持部分對象和存活,在一些情況下將負責代表部分的對象湮滅掉。代表整體的對象可以將代表部分的對象傳遞給另一個對象,由後者負責此對象的生命週期。換言之,代表部分的對象在每一個時刻只能與一個對象發生組合關係,由後者排他地負責生命週期。部分和整體的生命週期一樣。

對於我們寫出來的.class文件,即對應應用程序加載器,它在加載時,首先會向上委託,委託給他的父親,即擴展類加載器,擴展類加載器繼續向上委託,給啓動類加載器,而啓動類加載器沒有父類,則在啓動類加載器查找是否有這個類,有則加載,無則打回;然後在擴展類加載器中找,有則加載,無則打回;最後在應用程序類加載器中加載。

雙親委派機制的優勢:採用雙親委派模式的是好處是Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係,通過這種層級關可以避免類的重複加載,當父親已經加載了該類時,就沒有必要子ClassLoader再加載一次。其次是考慮到安全因素,java核心api中定義類型不會被隨意替換,假設通過網絡傳遞一個名爲java.lang.Integer的類,通過雙親委託模式傳遞到啓動類加載器,而啓動類加載器在覈心Java API發現這個名字的類,發現該類已被加載,並不會重新加載網絡傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class,這樣便可以防止核心API庫被隨意篡改。

我們以一個簡單的代碼爲例:
在這裏插入圖片描述
我們自己定義一個String類,注意在java.lang包中也有一個系統自帶的String類,而我們定義的這個類,包名也叫做java.lang,運行結果怎麼樣呢 ?
在這裏插入圖片描述
爲啥會找不到main方法?
這裏就要從雙親委託機制進行解釋:我們定義的這個類在加載時首先是對應應用程序類加載器,它要向上委託,直到啓動類加載器。
上面已經說過,啓動類加載器加載的是位於jre/lib目錄下的核心類庫,其中就有String類,這個String類就在java.lang 中,所以我們自定義的這個String類在最上層的啓動類加載器中被核心類庫裏的String類替換掉了!

參考文章:https://blog.csdn.net/m0_38075425/article/details/81627349

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章