1. 程式人生 > >深入理解JAVA虛擬機器學習筆記14——類載入的準備和解析

深入理解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指令以外,虛擬機器會對第一次解析結果進行快取,記錄解析狀態,避免重複解析。

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

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