Java虛擬機類裝載:原理、實現與應用
Java虛擬機類裝載:原理、實現與應用 By jorren 發表于 2005-12-21 15:13:00 |
||
一、引言 Java虛擬機(JVM)的類裝載就是指將包含在類文件中的字節碼裝載到JVM中, 并使其成為JVM一部分的過程。JVM的類動態裝載技術能夠在運行時刻動態地加載或者替換系統的某些功能模塊, 而不影響系統其他功能模塊的正常運行。本文將分析JVM中的類裝載系統,探討JVM中類裝載的原理、實現以及應用。 二、Java虛擬機的類裝載實現與應用 2.1 裝載過程簡介 所謂裝載就是尋找一個類或是一個接口的二進制形式并用該二進制形式來構造代表這個類或是這個接口的class對象的過程,其中類或接口的名稱是給定了的。當然名稱也可以通過計算得到,但是更常見的是通過搜索源代碼經過編譯器編譯后所得到的二進制形式來構造。 在Java中,類裝載器把一個類裝入Java虛擬機中,要經過三個步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗、準備和解析三步,除了解析外,其它步驟是嚴格按照順序完成的,各個步驟的主要工作如下: 裝載:查找和導入類或接口的二進制數據; 至于在類裝載和虛擬機啟動的過程中的具體細節和可能會拋出的錯誤,請參看《Java虛擬機規范》以及《深入Java虛擬機》,它們在網絡上面的資源地址是: 2.2 裝載的實現 JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader 是一個重要的Java運行時系統組件。它負責在運行時查找和裝入類文件的類。 在Java中,ClassLoader是一個抽象類,它在包java.lang中,可以這樣說,只要了解了在ClassLoader中的一些重要的方法,再結合上面所介紹的JVM中類裝載的具體的過程,對動態裝載類這項技術就有了一個比較大概的掌握,這些重要的方法包括以下幾個: ①loadCass方法 loadClass(String name ,boolean resolve)其中name參數指定了JVM需要的類的名稱,該名稱以包表示法表示,如Java.lang.Object;resolve參數告訴方法是否需要解析類,在初始化類之前,應考慮類解析,并不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要解析。這個方法是ClassLoader 的入口點。 ②defineClass方法 這個方法接受類文件的字節數組并把它轉換成Class對象。字節數組可以是從本地文件系統或網絡裝入的數據。它把字節碼分析成運行時數據結構、校驗有效性等等。 ③findSystemClass方法 findSystemClass方法從本地文件系統裝入文件。它在本地文件系統中尋找類文件,如果存在,就使用defineClass將字節數組轉換成Class對象,以將該文件轉換成類。當運行Java應用程序時,這是JVM 正常裝入類的缺省機制。 ④resolveClass方法 resolveClass(Class c)方法解析裝入的類,如果該類已經被解析過那么將不做處理。當調用loadClass方法時,通過它的resolve 參數決定是否要進行解析。 ⑤findLoadedClass方法 當調用loadClass方法裝入類時,調用findLoadedClass 方法來查看ClassLoader是否已裝入這個類,如果已裝入,那么返回Class對象,否則返回NULL。如果強行裝載已存在的類,將會拋出鏈接錯誤。 2.3 裝載的應用 一般來說,我們使用虛擬機的類裝載時需要繼承抽象類java.lang.ClassLoader,其中必須實現的方法是loadClass(),對于這個方法需要實現如下操作:(1) 確認類的名稱;(2) 檢查請求要裝載的類是否已經被裝載;(3) 檢查請求加載的類是否是系統類;(4) 嘗試從類裝載器的存儲區獲取所請求的類;(5) 在虛擬機中定義所請求的類;(6) 解析所請求的類;(7) 返回所請求的類。 所有的Java 虛擬機都包括一個內置的類裝載器,這個內置的類庫裝載器被稱為根裝載器(bootstrap ClassLoader)。根裝載器的特殊之處是它只能夠裝載在設計時刻已知的類,因此虛擬機假定由根裝載器所裝載的類都是安全的、可信任的,可以不經過安全認證而直接運行。當應用程序需要加載并不是設計時就知道的類時,必須使用用戶自定義的裝載器(user-defined ClassLoader)。下面我們舉例說明它的應用。 public abstract class MultiClassLoader extends ClassLoader{ /* 類s已經在本地內存*/ /* 取得類s的字節數組*/ /* 將類字節數組轉換為類*/ /* 將新加載的類放入本地內存*/ /* 返回已裝載、解析的類*/ 前面我們已經知道,一個Java應用程序使用兩種類型的類裝載器:根裝載器(bootstrap)和用戶定義的裝載器(user-defined)。根裝載器是Java虛擬機實現的一部分,舉個例子來說,如果一個Java虛擬機是在現在已經存在并且正在被使用的操作系統的頂部用C程序來實現的,那么根裝載器將是那些C程序的一部分。根裝載器以某種默認的方式將類裝入,包括那些Java API的類。在運行期間一個Java程序能安裝用戶自己定義的類裝載器。根裝載器是虛擬機固有的一部分,而用戶定義的類裝載器則不是,它是用Java語言寫的,被編譯成class文件之后然后再被裝入到虛擬機,并像其它的任何對象一樣可以被實例化。 Java類裝載器的體系結構如下所示: 小應用程序(Applet) 裝載器: 裝載器為系統裝載器,它從用戶指定的網絡上的特定目錄裝載小應用程序代碼。 在設計一個類裝載器的時候,應該滿足以下兩個條件: 對于相同的類名,類裝載器所返回的對象應該是同一個類對象 如果類裝載器CL1將裝載類C的請求轉給類裝載器CL2,那么對于以下的類或接口,CL1和CL2應該返回同一個類對象:a)S為C的直接超類;b)S為C的直接超接口;c)S為C的成員變量的類型;d)S為C的成員方法或構建器的參數類型;e)S為C的成員方法的返回類型。 Java中的類的裝載過程也就是代理裝載的過程。比如:Web瀏覽器中的JVM需要裝載一個小應用程序TestApplet。JVM調用小應用程序裝載器ACL(Applet ClassLoader)來完成裝載。ACL首先請求它的父裝載器, 即系統裝載器裝載TestApplet是否裝載了這個類, 由于TestApplet不在系統裝載器的裝載路徑中, 所以系統裝載器沒有找到這個類, 也就沒有裝載成功。接著ACL自己裝載TestApplet。ACL通過網絡成功地找到了TestApplet.class 文件并將它導入到了JVM中。在裝載過程中, JVM發現TestAppet是從超類java.applet.Applet繼承的。所以JVM再次調用ACL來裝載java.applet.Applet類。ACL又再次按上面的順序裝載Applet類, 結果ACL發現他的父裝載器已經裝載了這個類, 所以ACL就直接將這個已經裝載的類返回給了JVM , 完成了Applet類的裝載。接下來,Applet類的超類也一樣處理。最后, TestApplet及所有有關的類都裝載到了JVM中。 四、結論 類的動態裝載機制是JVM的一項核心技術, 也是容易被忽視而引起很多誤解的地方。本文介紹了JVM中類裝載的原理、實現以及應用,尤其分析了ClassLoader的結構、用途以及如何利用自定義的ClassLoader裝載并執行Java類,希望能使讀者對JVM中的類裝載有一個比較深入的理解 |