JavaRush /Java Blog /Random-TW /類別如何載入到 JVM 中
Aleksandr Zimin
等級 1
Санкт-Петербург

類別如何載入到 JVM 中

在 Random-TW 群組發布
在完成了程式設計師工作中最困難的部分並編寫了“Hello World 2.0”應用程式後,剩下的就是組裝分發工具包並將其傳輸給客戶,或至少傳輸給測試服務。在發行版中,一切都按其應有的方式進行,當我們啟動程式時,Java 虛擬機器就出現了。眾所周知,虛擬機器以字節碼的形式讀取類別檔案中提供的命令,並將它們轉換為處理器的指令。我建議了解一下字節碼進入虛擬機器的方案。

類別載入器

它用於向 JVM 提供編譯後的字節碼,該字節碼通常儲存在擴展名為 的檔案中.class,但也可以從其他來源獲取,例如透過網路下載或由應用程式本身產生。 類別是如何載入到 JVM 中的 - 1根據Java SE規範,為了讓程式碼在JVM中運行,需要完成三個步驟:
  • 從資源載入字節碼並建立類別的實例Class

    這包括在先前載入的類別中搜尋請求的類,取得用於載入的字節碼並檢查其正確性,建立該類別的實例(以便在Class運行時使用它)以及載入父類。如果父類別和介面尚未加載,則認為該類別未加載。

  • 綁定(或連結)

    根據規範,這個階段又分為三個階段:

    • 驗證,檢查接收到的字節碼的正確性。
    • 準備工作,為靜態欄位分配 RAM 並使用預設值初始化它們(在這種情況下,明確初始化(如果有)已經在初始化階段發生)。
    • 解析,類型、欄位和方法的符號連結解析。
  • 初始化接收到的對象

    在這裡,與前面的段落不同,一切似乎都清楚應該發生什麼。當然,準確地理解這是如何發生的會很有趣。

所有這些步驟均按以下要求順序執行:
  • 在連結之前必須完全載入該類別。
  • 一個類別在初始化之前必須經過充分的測試和準備。
  • 連結解析錯誤在程式執行期間​​發生,即使它們是在連結階段偵測到的。
如您所知,Java 實作了類別的延遲(或惰性)載入。這意味著在應用程式遇到對它們的明確引用之前,不會執行已載入類別的引用欄位的類別的載入。換句話說,解析符號連結是可選的,預設不會發生。然而,JVM 實作也可以使用能量類別來加載,即 必須立即考慮所有符號連結。正是出於這一點,最後一個要求才適用。另外值得注意的是,符號連結的解析不依賴類別載入的任何階段。一般來說,每個階段都值得好好研究;讓我們試著找出第一個階段,就是載入字節碼。

Java 載入器的類型

Java 中有三個標準載入器,每個載入器從特定位置載入一個類別:
  1. Bootstrap是一個基本的載入器,也稱為Primordial ClassLoader。

    從 rt.jar 檔案載入標準 JDK 類

  2. 擴充類別載入器——擴充載入器。

    載入擴充類,預設位於 jre/lib/ext 目錄中,但可以透過 java.ext.dirs 系統屬性設定

  3. 系統類別載入器——系統載入器。

    載入 CLASSPATH 環境變數中定義的應用程式類

Java 使用類別載入器的層次結構,其中根當然是基礎類別載入器。接下來是擴充加載器,然後是系統載入器。當然,每個載入器都會儲存一個指向父載入器的指針,以便在它本身無法執行此操作的情況下能夠將載入委託給它。

抽象類別類別載入器

除了基底類別之外,每個載入器都是抽象類別的後代java.lang.ClassLoader。例如,擴充載入器的實作是 類sun.misc.Launcher$ExtClassLoader,系統載入器是sun.misc.Launcher$AppClassLoader。基本載入器是本機的,其實作包含在 JVM 中。任何擴展的類別都java.lang.ClassLoader可以提供自己的方式來載入二十一點和這些相同的類別。為此,需要重新定義相應的方法,目前我只能粗淺地考慮一下,因為 我沒有詳細了解這個問題。他們來了:
package java.lang;
public abstract class ClassLoader {
    public Class<?> loadClass(String name);
    protected Class<?> loadClass(String name, boolean resolve);
    protected final Class<?> findLoadedClass(String name);
    public final ClassLoader getParent();
    protected Class<?> findClass(String name);
    protected final void resolveClass(Class<?> c);
}
loadClass(String name)少數公共方法之一,它是載入類別的入口點。它的實作歸結為呼叫另一個受保護的方法loadClass(String name, boolean resolve),該方法需要被重寫。如果您查看此受保護方法的 Javadoc,您可以理解如下內容:提供兩個參數作為輸入。一種是需要載入的類別的二進位名稱(或完全限定的類別名稱)。類別名稱是用所有包的列表指定的。第二個參數是一個標誌,決定是否需要符號連結解析。預設情況下它是false,這意味著使用延遲類別載入。此外,根據文檔,在該方法的預設實現中,進行了一個調用findLoadedClass(String name),該調用檢查先前是否已加載該類,如果是,則返回對該類的引用。否則,將呼叫父載入器的類別載入方法。如果沒有一個載入器可以找到已載入的類,則每個載入器都會按照相反的順序嘗試查找並載入該類,從而覆蓋findClass(String name). 這將在「類別載入方案」一章中更詳細地討論。最後,最後但並非最不重要的一點是,在類別載入後,根據解析標誌,將決定是否透過符號連結載入類別。一個明顯的例子是,可以在類別載入階段呼叫Resolution階段。因此,透過擴展類別ClassLoader並重寫其方法,自訂載入器可以實現自己的邏輯,以將字節碼傳遞到虛擬機器。Java 也支援「目前」類別載入器的概念。目前載入器是載入目前正在執行的類別的載入器。每個類別都知道它是用哪個載入器載入的,您可以透過呼叫其String.class.getClassLoader(). 對於所有應用程式類,「目前」載入器通常是系統載入器。

類別載入的三個原則

  • 代表團

    載入類別的請求將傳遞給父載入器,並且僅當父載入器無法找到並載入該類別時,才會嘗試載入該類別本身。這種方法允許您使用盡可能接近基類的載入器來載入類別。這實現了最大的班級可見性。每個載入器都會保留其載入的類別的記錄,並將它們放置在其快取中。這些類別的集合稱為範圍。

  • 能見度

    載入器只能看到「它的」類別和「父類」的類,並且不知道其「子類」載入的類別。

  • 獨特性

    一個類別只能載入一次。委託機制確保啟動類別載入的載入器不會重載先前載入到 JVM 中的類別。

因此,在編寫引導程式時,開發人員應該遵循這三個原則。

類別載入方案

當發生載入類別的呼叫時,會在目前載入器已載入的類別的快取中搜尋該類別。如果先前尚未載入所需的類,則委託原則會將控制權轉移給位於層級結構中更高一級的父載入器。父載入器也會嘗試在其快取中尋找所需的類別。如果該類別已載入且載入器知道其位置,則將Class傳回該類別的物件。如果沒有,搜尋將繼續,直到到達基本引導程式。如果基底載入器沒有所需類別的資訊(即尚未載入),則將在給定載入器知道的類別的位置中搜尋該類別的字節碼,如果該類別無法載入後,控制權將返回到子載入器,子載入器將嘗試從已知的來源載入。如上所述,基本載入器的類別位置是 rt.jar 函式庫,擴充載入器的類別位置是 jre/lib/ext 擴充的目錄,系統類別的位置是 CLASSPATH,使用者類別的位置可以是不同的。因此,載入類別的進度是相反的方向——從根加載器到當前載入器。當找到類別的字節碼時,該類別將載入到 JVM 中並獲得該類型的實例Class。正如您所看到的,所描述的載入方案與上述方法的實作類似loadClass(String name)。下面你可以在圖中看到這個圖。
類別是如何載入到 JVM 中的 - 2

作為結論

在學習語言的第一步中,沒有特別需要了解Java中類別是如何載入的,但是了解這些基本原理將有助於您在遇到諸如 或 之類的錯誤時避免ClassNotFoundException絕望NoClassDefFoundError。好吧,或至少大致了解問題的根源是什麼。ClassNotFoundException因此,當在程式執行期間​​動態載入類別時,如果載​​入器無法在快取中或沿著類別路徑找到所需的類,則會發生異常。但該錯誤NoClassDefFoundError更為嚴重,當所需的類別在編譯期間可用但在程式執行期間​​不可見時,就會發生該錯誤。如果程式忘記包含它使用的庫,就會發生這種情況。嗯,了解您在工作中使用的工具的結構原理(不一定是清楚而詳細地深入了解)這一事實本身就可以讓您更清楚地理解該機制內部發生的過程,這在轉向,導致自信地使用這個工具。

來源

Java 中的類別載入器如何運作 整體而言,這是一個非常有用的來源,具有易於理解的資訊表示形式。 載入類,ClassLoader 這是一篇相當長的文章,但重點是如何使用這些相同的載入器實作您自己的實作。 ClassLoader:類別的動態載入 不幸的是,這個資源現在不可用,但是我在那裡找到了最容易理解的類別載入方案圖,所以我忍不住要添加它。 Java SE 規格:第 5 章:載入、連結與初始化
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION