0035-對象實例化步驟,內存佈局,訪問定位

1. 實例化步驟

  1. 對象創建的方式
1. new

2. Class的newInstance()

3. Contructor的newInstance(xxx)

4. 使用clone

5. 使用反序列化

6. 第三方庫Objenesis
  1. 對象的創建步驟
1. 判斷對象對應的類是否加載,鏈接,初始化,沒有加載,使用雙親委派機制加載類

2. 爲對象分配內存
    不同的垃圾收集器會導致,內存規整或不規整,不同的內存結構,會有不同的分配方式
    2.1 內存規整-指針碰撞
    2.2 內存不規整-維護空閒列表
    
3. 處理併發安全問題,內存分配必須是線程安全的,同一塊堆內存,同一時間只能由一個線程使用
    3.1 採用CAS保證原子性
    3.2 分配TLAB
    
4. 初始化分配到的空間-零值初始化,爲所有屬性設置默認值, 保證對象實例字段不賦值可以直接使用

5. 設置對象的對象頭

6. 執行init方法進行初始化(構造方法)

2. 內存佈局

在java堆內存上的對象由三部分組成

1. 對象頭

2. 實例數據

3. 對齊填充
  1. 對象頭
    包含兩部分數據,如果是數組,還要記錄數組的長度
1. 運行時元數據(Mark Word)
    1.1 哈希值(HashCode)
    1.2 GC分代年齡
    1.3 鎖狀態標誌
    1.4 線程持有的鎖
    1.5 偏向線程ID
    1.6 偏向時間戳
    
2. 類型指針
    指向類元數據InstanceKlass,確定該對象所屬的類型
  1. 實例數據
    對象真正存儲有效信息的地方,包括程序代碼中定義的各種類型的字段,包括從父類繼承下來的和本身擁有的字段

存儲規則

1. 相同寬度的字段總是被分配到一起

2. 父類中定義的變量會出現在子類之前

3. 如果CompactFields參數爲true,子類的窄變量可能插入到父類變量的空隙
  1. 對齊填充
    沒有特殊含義,僅僅起到佔位符的作用

  2. 實例

public class Customer{
    int id = 1001;
    String name;
    Account acct;

    {
        name = "匿名客戶";
    }
    public Customer(){
        acct = new Account();
    }

}
class Account{

}

上述代碼的內存佈局如下圖所示


在這裏插入圖片描述

1. main方法的棧幀被壓入棧,棧幀中的局部變量有args和cust兩個變量

2. cust指向堆空間的Custtomer實例

3. Customer實例包含三部分信息:對象頭,實例數據和對齊填充

4. 對象頭重的類型信息指向方法區的類元信息

5. 實例數據的字符串指向字符串常量池

3. 對象訪問定位

jvm如何通過棧幀中的對象引用訪問到其內部的對象實例
在這裏插入圖片描述

訪問方式

  • 句柄訪問


    在這裏插入圖片描述
1. java堆區中額外維護一份句柄信息,指向堆中對象和對象類型

2. 虛擬機棧中的局部變量表保存句柄信息
  • 直接指針(HotSpot採用)


    在這裏插入圖片描述

棧中局部變量直接保存對象指針信息

兩種訪問方式比較

1. 直接指針方式,不維護句柄信息,節省空間,訪問更快

2. 使用句柄池方式,棧中局部變量保存的地址始終不用變動
    堆中對象移動時,直接地址方式保存地址需要改變地址,句柄方式則不用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章