周日的上午,筆者像往常一樣盯著電腦發呆,想起昨晚的夢中,一個身著西裝的面試官問道:你知道new一個物件時,Java虛擬機是怎么作業的嗎?
我:

沒辦法,那今天就復習一下好了,

類的生命周期分為7個階段:加載、驗證、準備、決議、初始化、使用、卸載,
下圖是筆者自定義的類,
class man {
String s;
public man(String string){
s = string;
}
}
public class Solution{
public void test(){
man a = new man("abc");
return;
}
}
利用jclasslib工具可以看到test方法的位元組碼檔案,
0 new #7 <test/man>
3 dup
4 ldc #9 <abc>
6 invokespecial #11 <test/man.<init>>
9 astore_1
10 return
下面結合位元組碼檔案對該類的生命周期進行分析,
加載:加載階段發生在第一步之前,主要作用有:
-
將類的位元組碼載入方法區中,內部采用 C++ 的 instanceKlass 描述 java 類,它的重要 field 有:
-
_java_mirror 即 java 的類鏡像,例如對 String 來說,就是 String.class,作用是把 klass 暴露給 java 使用
-
_super 即父類
-
_fields 即成員變數
-
_methods 即方法
-
_constants 即常量池
-
_class_loader 即類加載器
-
_vtable 虛方法表
-
_itable 介面方法表
-
-
在堆中生成一個代表這個類的java.lang.Class物件(比如樣例中即為man.Class物件),作為方法區這個類的各種資料的訪問入口,
驗證:驗證階段同樣發生在第一步之前,主要作用為驗證類是否符合 JVM規范,安全性檢查,(注意:驗證階段往往和加載階段同時進行)
準備:當一個類驗證通過時,虛擬機就會進入準備階段,在這個階段,虛擬機就會為這個類分配相應的記憶體空間,并設定初始化值,
決議:將常量池中的類、介面、欄位、方法等符號參考轉為直接參考,比如類中定義了一個方法,該方法一開始作為符號參考存盤于常量池中,決議后可在常量池中直接定位到該方法在記憶體中的真實地址,(注意:《Java虛擬機規范》并未規定決議階段發生的具體時間,只要求在invokespecial等17個用于運算子號參考的位元組碼指令之前,對它們使用的符號參考進行決議,因此,決議有時發生在初始化階段之后)
注意:驗證、準備、決議三個階段統稱為鏈接階段,其中驗證階段往往與加載階段同時進行,
之后正式進入初始化階段,并開始執行位元組碼檔案中的內容,
初始化:是類加載程序的最后一個步驟,直到初始化階段,Java虛擬機才真正開始執行類中撰寫的程式代碼,
下面從位元組碼檔案入手,分析整個程序,
首先是
new #7 <test/man>
在該執行緒的虛擬機堆疊的運算元堆疊壓入了指向之前分配的man的實體的記憶體的地址供任何下面的操作來呼叫,
dup
復制運算元堆疊的堆疊頂值,并將其壓入堆疊頂,也就是說此時運算元堆疊上有連續相同的兩個物件地址,
ldc #9 <abc>
將String型常量值"abc"從常量池中推送至堆疊頂,
//在該條位元組碼指令之前決議常量池中的#11即可
invokespecial #11 <test/man.<init>>
呼叫類的構造方法,(注意:這里將之前壓入運算元堆疊的"abc"取出來作為實參傳入了類的構造方法,接著從運算元堆疊頂彈出一個實體物件的參考,即將物件創建在之前入堆疊的物件地址上)
astore_1
從運算元堆疊頂取出 man 物件的參考并存到區域變數表,
return
最后由return指令結束方法,
以上便是全部的類加載程序,如有紕漏,歡迎指正,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/275139.html
標籤:java
上一篇:Java中的變數和基本資料型別
下一篇:多執行緒實作生產者消費者案例
