深入理解JAVA虛擬機學習筆記14——類加載的準備和解析

每天進步一點點!

今天我們一起看一下類加載的準備階段和解析階段。

先看一下準備階段:主要任務是在方法區中爲類變量(僅static修飾變量,不包含實例變量)分配內存並設置類變量初始化的階段。

這裏面的區別,我們通過下面的代碼來簡單瞭解一下。

我們將上面的代碼編譯好後,通過字節碼工具看一下其中的信息。

首先,從上圖可以看出,被final修飾的b是直接賦值的。

我們再打開classlib,如下圖所示,b對應的是一個ConstantValue常量,而不是一個引用,對應的,在準備階段,虛擬機就會將常量值2賦給b。

而對於只有static修飾的變量a,在準備階段,將初始化爲0。如下圖所示,在執行構造器方法clinit()的時候纔會把把1複製給a。

到了變量c這裏,則又不一樣。它在準備階段是不會進行任何操作的。如下圖所示,到了對象構造方法init()中,纔會把值3賦給c。

類的普通變量在對象初始化的時候隨着對象分配到Java堆中。

下面再看一下解析階段:虛擬機的主要任務是將常量池內的符號引用替換爲直接引用。

前面也已經提過了引用的概念,這裏讓我們再複習一下(以下概念引自《深入理解Java虛擬機》)。

符號引用(Symbolic References):以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。

直接引用(Direct References):直接引用可以是直接指向目標的指針、相對偏移量或是一個能直接定位到目標的句柄。

另外,符號引用的的目標不一定已經加載到內存中,而直接引用的目標必然已經加載到內存中。

以上是對這兩種引用的定義,筆者談一下自己的理解。

對於虛擬機能直接使用的類型,比如基本數據類型的char,int,long,double等定義的變量引用,都是直接引用,比如定義變量int a = 1。

而當變量的引用指向一個類的時候,比如定義Test test = new Test(),jvm只會存儲這個類對應的符號com.xkx.test.Test,而不會存儲具體的類信息,這就是符號引用。

掌握了引用的概念之後,其實解析就比較簡單了,下面簡單地瞭解一下。

需要解析的內容包含以下四個部分:

1、類或接口的解析

2、字段解析

3、類方法解析

4、接口方法解析

以類或接口的解析爲例,包含以下三個步驟:

1. 如果該符號引用不是一個數組類型,那麼虛擬機將會把該符號代表的全限定名稱傳遞給調用這個符號引用的類。這個過程由於涉及驗證過程所以可能會觸發其他相關類的加載。

2. 如果該符號引用是一個數組類型,並且該數組的元素類型是對象。我們知道符號引用是存在方法區的常量池中的,該符號引用的描述符會類似”[java/lang/Integer”的形式,將會按照上面的規則進行加載,虛擬機將會生成一個代表此數組對象的直接引用。

3. 如果上面的步驟都沒有出現異常,那麼該符號引用已經在虛擬機中產生了一個直接引用,但是在解析完成之前需要對符號引用進行驗證,主要是確認當前調用這個符號引用的類是否具有訪問權限,如果沒有訪問權限將拋出java.lang.IllegalAccess異常。

剩下的部分解析步驟和類與接口的解析步驟大同小異,這裏就不再贅述了,有興趣的可以自行百度一下。

另外,有一點需要提一下,除invokedynamic指令以外,虛擬機會對第一次解析結果進行緩存,記錄解析狀態,避免重複解析。

今天的學習就到這裏了,筆者認爲初始化階段是比較貼近於實際的內容,下一篇將單獨整理出來和大家來分享。

喜歡文章或想一起學習的朋友可以關注我,給我點贊,我將會持續更新,有什麼疑問或文中有不當之處請給我留言,真誠地希望能與大家一起交流探討,學習進步。


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