1. 程式人生 > >HotSPot虛擬機器物件探祕

HotSPot虛擬機器物件探祕

  當虛擬機器遇到一條含有new的指令時,會進行一系列物件建立的操作

  1.檢查常量池中是否有要建立的這個物件所屬類的符號引用

  ·若無,說明這個類還沒有被定義!拋ClassNotFoundException

  ·若有,轉2

  1.檢查這個符號引用所代表的類是否已被JVM載入

  ·若否,就找該類的class檔案,並載入進方法區

  ·若是,轉3

  1.根據方法區中該類的資訊確定該類所需的記憶體大小

  一個物件所需的記憶體大小是在這個物件所屬類被定義完就能確定的!

  且一個類所生產的所有物件的記憶體大小是一樣的!

  JVM在一個類被載入進方法區的時候就知道該類生產的每一個物件所需要的記憶體大小

  從堆中劃分一塊對應大小的記憶體空間給新的物件,分配堆中記憶體有兩種方式

  ·指標碰撞(Bump the Pointer)

  如果JVM的垃圾收集器採用複製演算法或標記-整理演算法,那麼堆中空閒記憶體是完整的區域,並且空閒記憶體和已使用記憶體之間由一個指標標記.

  那麼當為一個物件分配記憶體時,只需移動指標即可.因此,這種在完整空閒區域上通過移動指標來分配記憶體的方式就叫做指標碰撞

  ·空閒列表 (Free List)

  如果JVM的GC器採用標記-清除演算法,那麼堆中空閒區域和已使用區域交錯,因此需要用一張“空閒列表”來記錄堆中哪些區域是空閒區域,從而在建立物件的時候根據這張“空閒列表”找到空閒區域,並分配記憶體

  綜上所述:JVM究竟採用哪種記憶體分配方法,取決於它使用了何種GC器

  為物件中的成員變數賦上初始值(預設初始化)

  1.設定物件頭(Object Header)

  2.呼叫物件的建構函式進行初始化

  至此,整個物件的建立過程就完成了

  一個物件從邏輯角度看,由域和方法構成

  從物理角度來看,物件是儲存在堆中的一串二進位制數

  物件在記憶體中儲存的佈局分三部分

  ·物件頭(Header)

  ·例項資料(Instance Data)

  ·對齊補充(Padding)

  ·儲存物件在執行過程中自身所需要的一些資料

  雜湊碼、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等

  ·型別指標

  即物件指向它的類元資料的指標,JVM通過該指標能確定這個物件是哪個類的例項.

  另外,如果物件是一個數組,那麼物件頭中還要包含陣列長度(因為從陣列的元資料無法確定陣列的大小).

  例項資料部分就是程式定義的各種欄位的內容,包含父/子類的都會記錄下來

  HotSpot要求物件的大小必須是8位元組的整數倍

  由於物件起始地址必須是8位元組的整數倍,但例項資料部分的長度是任意的,因此需要對齊補充欄位確保整個物件的總長度為8的整數倍

  棧上的reference資料存放的是一個地址,那麼根據地址型別的不同,物件有不同的訪問方式

  ·控制代碼訪問方式

  Java堆中需要有一塊叫做控制代碼池的記憶體,存放所有物件的地址和所有物件所屬類的類資訊

  reference中存放的是物件在控制代碼池中的地址.

  訪問物件時,首先需要通過reference找到該物件的控制代碼,然後根據控制代碼中物件的地址再訪問物件

  ·直接指標訪問方式

  reference直接存放物件地址,從而不需要控制代碼池,通過引用能夠直接訪問物件

  但物件所在的記憶體空間中需要額外的策略儲存物件所屬的類資訊的地址

  ·兩種方式的比較

  使用控制代碼最大好處是reference中儲存的是穩定的控制代碼地址,在物件被移動時也只改變控制代碼中的例項資料指標,而reference本身不需要修改

  而HotSpot採用直接指標訪問方式,因為它只需一次定址操作,節省了一次指標定位的時間開銷,物件的訪問又十分頻繁,從而效能比控制代碼訪問方式快一倍