深入理解Java虛擬機器筆記---類載入過程,深入理解虛擬機器

來源:互聯網
上載者:User

深入理解Java虛擬機器筆記---類載入過程,深入理解虛擬機器
一、載入
   “載入”(Loading)階段是“類載入”(Class Loading)過程的一個階段。在載入階段,虛擬機器需要完成以下三件事情:
a.通過一個類的全限制名來擷取定義此類的二進位位元組流。
b.將這個位元組流所代表的靜態儲存結構轉化為方法區的運行進資料結構。
c.在Java堆中產生一個代表這個類的java.lang.Class對象,作為方法區這些資料的訪問入口。
   虛擬機器規範的這三點要求實際上並不具體,因此虛擬機器實現與具體應用的靈活度相當大。例如“通過一個類的全限制名來擷取定義此類的二進位位元組流”,並沒有指明二進位位元組流要從一個Class檔案中擷取,準確地說是根本沒有指明要從哪裡擷取及怎樣擷取。虛擬機器設計團隊載入階段搭建了一個相當開放的,廣闊的舞台,Java發展曆程中,許多舉足輕重的Java技術都建立在這一基礎上,例如:
a.從ZIP包中讀取,這很常見,最終成為日後JAR,EAR,WAR格式的基礎
b.從網路中擷取,這種情境最典型的應用就是Applet
c.運行時計算產生,這種情境使用得最多的就是動態代理技術,在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generatProxyClass來為特定介面產生 *$Proxy的代理類的二進位位元組流。
d.由其它檔案產生,典型情境:JSP應用。
e.從資料庫中讀取,這種情境相對少見些,有些中介軟體伺服器(如SAP Netweaver)可以選擇把程式安裝到資料庫中來完成程式碼在叢集間的分發。
f. .......

   載入階段與串連階段的部分內容(如一部分位元組碼檔案格式驗證動作)是交叉進行的,載入階段尚未完成,串連階段可能已經開始,但這些夾在載入階段之中進行的動作,仍然屬性串連階段的內容,這兩階段的開始時間仍然保持著固的先後順序。

二、驗證
   驗證是串連階段的第一步,這一階段的目的是為了確保Class檔案的位元組流中包含的資訊符合當前虛擬機器的要求,並且不會危害虛擬機器自身的安全。
   儘管驗證階段是非常重要的,並且驗證階段的工作量在虛擬機器的類載入子系統中佔了很大一部分。如果驗證到輸入的位元組流不符合Class檔案的儲存格式,就拋出一個java.lang.VerifyError錯誤或者其子錯誤。具體應當檢查哪些方面,如何檢查,何時檢查,虛擬機器規範都沒有明確說明,所以不同的虛擬機器對驗證的實現可能會有所不同,但大致上都會完成四個階段的驗證過程:檔案格式驗證,中繼資料驗證,位元組碼驗證和符號引用驗證。

三、準備
   準備階段是正式為類變數分配記憶體並設定類變數初始值的階段,這些記憶體都將在方法區中進行分配。需要強調的是:首先是這時候進行記憶體配置的僅包括類變數,而不包括執行個體變數,執行個體變數將會在對象執行個體化時隨著對象一起分配是Java堆中。其次是這裡所說的初始值“通常情況“下是資料類型的零值,假設一個類變數定義為:
public static int value = 123;
   那麼變數value在準備階段過後的初始值是0而不是123,因為這時候尚未開始執行任何Java方法,而把value賦值為123的putstatic指令是在程式被編譯後,存放於類構造器”<clinit>“方法中的,所以把value賦值為123的動作將在初始化階段才會被執行。
   上面提到的”通常情況“下初始值為零值,但是,如果類欄位的欄位屬性工作表中存在ConstantValue屬性,那在準備階段變數value就會初始初始化為ConstantValue屬性所指定的值,假設上面類變數value被定義為:
public static final int value = 123;
編譯時間Javac將會為value產生ConstantValue屬性工作表,在準備階段虛擬會就會根據ConstantValue的設定將value賦值為123。

四、解析
   解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程,在Class檔案中它以CONSTANT_Class_info,CONSTANT_Fieldref_info,CONSTANT_Methodref_info等類型的常量出現。直接引用與符號引用的關聯是:
a.符號引用(Symbolic References)以一組符號來描述所引用的目錄,符號可以任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機器實現的記憶體布局無關,引用的目標並不一定已經載入到了記憶體中。
b.直接引用可以是直接指向目標的指標,相對位移量或是一個能間接定位到目標的控制代碼。直接引用與虛擬機器實現的記憶體布局相關,同一個符號引用在不同的虛擬機器執行個體上翻譯出來的直接引用一般不會相同。如果有了直接引用,那引用的目標就必須已經在記憶體中存在。
   虛擬機器規範中並未規定解析階段發生的具體時間,只要求在執行了newarray,heckcast,getfield,etstatic,instanceof,invokeinterface,invokespecial,nvokestatic,invokevritual,multianewarray,new,putfield和putstatic這13個用於操作符號引用的位元組碼指令之前,先對它們所使用的符號引用進行解析。所以虛擬機器實現會根據需要來判斷,到底是在類載入器載入時就是常量池中的符號引用進行解析,還是等到一個符號引用將要被使用時才去解析它。
   同一個符號引用可能會進行多次解析請求,虛擬機器實現可能會對第一次解析的結果進行緩衝從而避免解析動作重複進行。無論是否真正執行了多次解析操作,虛擬機器需要保證的都是在同一個實體中,如果一個符號引用被成功解析過,那麼後續的解析請求就應當一直成功;同樣的,如果第一次解析失敗了,其它指令對這個符號引用的解析請求也應該收到相同的異常。
   解析動作主要針對的是類或介面,欄位,方法,介面方法四類符號引用進行的,分別對應於常量池的CONSTANT_Class_info,CONSTANT_Fieldref_info,CONSTANT_Methodref_info及CONSTANT_InterfaceMetodref_info四種常量類型。下面是這四種引用的解析過程。
1.類或介面的解析過程
   假設當前代碼所處的類為D,如果要把一個從未解析過的符號引用N解析為一個類或介面C的直接引用,那虛擬機器完成整個解析過程需要包括以下3個步驟:
a.如果C不是一個數群組類型,那虛擬機器將會把代表N的全限定名傳遞給D類的載入器去載入這個類C。在載入過程中,由於無資料驗證,位元組碼驗證的需要,又將可能觸發其它相關類的載入動作,例如載入這個類的父類或實現的介面。一旦這個載入過程出現了任何異常,解析過程就將宣告失敗。
b.如果C是一個數群組類型,並且數組的元素類型為對象,也就是N的描述符會是類似”[Ljava.lang.Integer"的形式,那麼會按照第a點的規則載入數組元素類型。如果N的描述符如前面所假設的形式,需要載入的元素類型就是“java.lang.Integer”,接著由虛擬機器產生一個代表此數組和元素的數組對象。
c.如果上面的步驟沒有出現任何異常,那麼C在虛擬機器中實際上已經成為一個有效類或介面了,但在解析完成之前還要進行符號引用驗證,確認C是否具備對D的存取權限,如果發現不具體存取權限,將拋出java.lang.IllegalAccessError錯誤。
2.欄位解析
   要解析一個未被解析過的欄位符號引用,首先將會對欄位表內class_index項中索引的CONSTANT_Class_info符號引用進行解析,也就是欄位所屬性的類或介面的符號引用。如果在解析這個類或介面符號引用過程中出現了任何異常,都會導致欄位符號引用解析失敗。如果解析成功完成,那麼這個欄位所屬性的類或介面用C表示,虛擬機器規範要求如下步驟對C進行後續欄位的搜尋:
a.如果C本身就包含了簡單名稱和欄位描述符都與目標相匹配的欄位,則返回這個欄位的直接引用,尋找結束。
b.否則,如果C中實現了介面,將會按照繼承關係從上往下遞迴搜尋各個介面和它的父介面,如果介面中包含了簡單名稱答欄位描述符都與目標相匹配的欄位,則返回該欄位的直接引用,尋找結束。
c.否則,如果C不是java.lang.Object的放,將會按照繼承關係從上往下遞迴搜尋其父類,如果在父類中包含了簡單名稱和欄位描述符都與目標匹配的欄位,則返回這個欄位的直接引用,尋找結束。
d.否則,尋找失敗,招拋出java.lang.NoSuchFieldError錯誤。
3.類方法解析
   類方法解析的第一個步驟與欄位解析一樣,也是需要先解析出方法表的class_index項中索引的方法所屬性類或介面的符號引用,如果解析成功,依然用C表示這個類,接下來虛擬機器將會按照如下步驟進行後續的類方法搜尋:
a.類方法和介面方法符號引用的常量類型定義是分開的,如果在類方法表中發現了class_index中索引的C是個介面,那麼直接就拋出java.lang.IncompatibleClassChangeError錯誤。
b.如果通了第a步,在類C中尋找是否有簡單名稱和描述符與目標相匹配的方法,如果有則返回這個方法的直接引用,尋找結束。
c.否則,在類C的父類中遞迴尋找是否有簡單名稱和欄位描述符都與目標匹配的方法,則返回這個方法的直接引用,尋找結束。
d.否則,在類C實現的介面列表及它們的父介面中遞迴尋找否有簡單名稱和欄位描述符都與目標匹配的方法,說明類C是一個抽象類別,這時候尋找結束,拋出java.lang.AbstractMethodError錯誤。
e.否則,宣告尋找失敗,拋出java.lang.NoSuchMethodError錯誤。
最後,如果尋找過程成功返回了直接引用,將會對暈個方法進行許可權驗證;如果發現不具務對此方法的存取權限,將拋出java.lang.IllegalAccessError錯誤。
4.介面方法解析
   介面方法也需要先解析出介面方法表中的class_index項中索引的方法所屬性的類或介面的符號引用,如果解析成功,依然用C表示這個介面,接下來虛擬機器將會按照如下步驟進行後續的介面訪求搜尋:
a.與類方法解析相反,如果在介面方法表中發現了class_index中索引的C是個類而不是介面,那麼直接就拋出java.lang.IncompatibleClassChangeError錯誤。
b.否則,在介面C中尋找是否有簡單名稱的描述符都與目標相匹配的方法,如果有則返回這個方法的直接引用,尋找結束。
c.否中,在介面C的父介面中遞迴尋找,直到java.lang.Object類為止,看是否有簡單名稱和描述符都與目標相匹配的方法,如果有則返回這個方法的直接引用,尋找結束。
d.否則,宣告尋找失敗,拋出java.lang.NoSuchMethodError錯誤。
由於介面中的所有方法都預設是public的,所以不存在存取權限問題,因此介面方法的符號解析應該不會拋出java.lang.IllegalAccessError錯誤。

五、初始化
   類的初始化是類載入過程的最後一步,前面的類載入動作,除了在載入階段使用者應用程式可以通過自訂類載入器參與之外,其餘動作完全由虛擬機器主導和控制。到了初始化階段,才真正執行類中定義的Java程式碼(或者說是位元組碼)。
   在準備階段,變數已經賦過一次系統要求的初始值,而在初始化階段,則是根據程式員通過程式制定的主觀計划去初始化類變數和其它資源,或者可以從另外一個角度來表達:初始化階段是執行類構造器<clinit>()方法的過程。<clinit>()方法執行過程可能會影響程式運行行為的一些特點與細節,如下:
1.<clinit>()方法是由編譯器自動收集類中所有類變數的賦值動作和靜態語句塊(static{})中的語句合并產生的,編譯器收集的順序是由語句在源檔案中出現的順序所決定的,靜態語句塊中只能訪問到定義在靜態語句塊之前的變數,定義在它之後變數,在前面的靜態語句塊中可以賦值,但是不能訪問。
2.<clinit>()方法與類的構造器<init>()不同,它不需要顯示地調用父類類構造器,虛擬機器會保證在子類的<clinit>()方法執行之前,父類的<clinit>()方法已經執行完畢。因此在虛擬機器中第一個被執行<clinit>()方法的類肯定是java.lang.Object。
3.由於父類的<clinit>()方法先執行,所就意味著父類中定義的靜態語句塊要優先於子類的類變數賦值操作。
4.<clinit>()方法對於類或介面來說並不是必須的,如果一個類中沒有靜態語句塊,也沒有對類變數的賦值操作,那麼編譯器可以不為這類產生<clinit>()方法。
5.介面中不能使用靜態語句塊,但仍然可以有變數初始化的同仁操作,因此介面與類一樣都會產生<clinit>()方法,但介面與類不同的是,執行介面的<clinit>()方法不需要先執行父介面的<clinit>()方法。只有當父介面中定義的變數被使用時,父介面才會初始化。另外,介面的實作類別在初始化時也不會執行介面的<clinit>()方法。
6.虛擬機器會保證一個類的<clinit>()方法在多線程環境中被正確地加鎖同步,如果多個線程同時去初始化一個類,那麼只會有一個線程去執行這個類的<clinit>()方法,其它線程都需要阻塞等待,直到活動線程執行<clinit>()方法完畢。如果在一個類的<clinit>()方法中有很耗時的操作,那就可能造成多個線程阻塞,在實際應用中這種阻塞往往是很隱形。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.