1. 程式人生 > >new一個物件都發生了什麼

new一個物件都發生了什麼

一. 之前沒有進行類載入

1. 類載入,同時初始化類中的靜態的屬性(賦預設值)

2. 執行靜態程式碼塊

3. 分配記憶體空間,同時初始化非靜態的屬性(賦預設值)。

4. 呼叫父類構造器

5. 父類構造器執行完後,如果自己宣告屬性的同時有顯示的賦值,那麼進行顯示賦值把預設值覆蓋。

6. 執行匿名程式碼塊

7. 執行構造器

8. 返回記憶體地址

二、之前已經進行了類載入

1. 分配記憶體空間,同時初始化非靜態的屬性(賦預設值)

2. 呼叫父類構造器

3. 父類構造器執行完後,如果自己宣告屬性的同時有顯示的賦值,那麼進行顯示賦值把預設值覆蓋

4. 執行匿名程式碼塊

5. 執行構造器

6. 返回記憶體地址

java建立物件的過程

一、檢測類是否被載入

虛擬機器遇到一條new指令時,首先去檢查這個指令的引數是否能夠在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被載入、解析和初始化。如果沒有,則去執行相應的類載入過程。

二、為新生物件分配記憶體

在類載入檢查以後,接下來虛擬機器將為新生物件分配記憶體。物件所需記憶體的大小在類載入完成後便可完全確定。

假設java堆中記憶體是絕對規整的,所有用過的記憶體都放在一邊,空閒的記憶體放在另一邊,中間放著一個指標作為分界點的指示器,那麼分配記憶體就僅僅是把那個指標向空閒空間那邊挪動一段與物件大小相等的距離,此分配方式稱為“指標碰撞”。

如果java堆中的記憶體並不是規整的,已使用的記憶體和空閒的記憶體相互交錯,那就沒有辦法簡單地進行指標碰撞了,虛擬機器就必須維護一個個列表,記錄上哪些記憶體塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給物件例項,並更新列表上的記錄,此分配方式稱為“空閒列表”。

選擇哪種分配方式是由java堆是否規整決定,而java堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。

三、初始化零值

記憶體分配完成後,虛擬機器需要將分配到的記憶體空間都初始化為零值(不包括物件頭),這一步操作保證了物件的例項欄位在java程式碼中可以不用賦初始值就直接使用,程式能訪問到這些欄位的資料型別所對應的零值。

四、進行必要的設定

接下來,虛擬機器要對物件進行必要的設定,例如這個物件是哪個類的例項,如何才能找到類的元資料資訊、物件的GC分代年齡等資訊。這些資訊存放在物件的物件頭中。

五、執行init方法

在上面的工作都完成以後,從虛擬機器的視角來說,一個新的物件已經產生了,但從java程式的視角來看,物件的建立才剛開始,<init>方法還沒有執行,所有的欄位都還為零值,所以一般來說,執行new指令之後會接著執行<init>方法,把物件按照程式設計師的意願進行初始化,這樣,一個真正可用的物件才算完全產生出來。

總結,建立一個物件的過程就是:

檢測類是否被載入,沒有載入的話先載入  ->  為新生物件分配記憶體  ->  將分配到的記憶體空間都初始化為零值  ->  對物件進行必要的設定  ->  執行<init>方法進行物件初始化 

使用控制代碼訪問物件的最大好處是reference中儲存的是穩定的控制代碼地址,在物件被移動(垃圾收集時移動物件是非常普遍的行為)時只會改變控制代碼中的例項資料指標,而reference本身不需要修改。

使用直接指標訪問的好處是速度更快,他節省了一次指標定位的時間開銷。

Sun HotSpot虛擬機器使用第二種方式進行物件訪問。