JVM視角看物件建立

微笑著生活發表於2018-08-12
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/qq_36367789/article/details/81605605

從jvm處理物件的流程來看,大概分成三步驟:1.如何建立。2.什麼是最佳儲存模型。3.如何訪問。本文將按照這三個流程進行講解。

一、物件的建立過程

1. 拿到記憶體建立指令

當虛擬機器遇到記憶體建立的指令的時候(new 類名),來到了方法區,找 根據new的引數在常量池中定位一個類的符號引用。

2. 檢查符號引用

檢查該符號引用有沒有被載入、解析和初始化過,如果沒有則執行類載入過程,否則直接準備為新的物件分配記憶體

3. 分配記憶體

虛擬機器為物件分配記憶體(堆)分配記憶體分為指標碰撞和空閒列表兩種方式;分配記憶體還要要保證併發安全,有兩種方式。

3.1. 指標碰撞

所有的儲存空間分為兩部分,一部分是空閒,一部分是佔用,需要分配空間的時候,只需要計算指標移動的長度即可。

3.2. 空閒列表

虛擬機器維護了一個空閒列表,需要分配空間的時候去查該空閒列表進行分配並對空閒列表做更新。

可以看出,記憶體分配方式是由java堆是否規整決定的,java堆的規整是由垃圾回收機制來決定的

3.2.5 安全性問題的思考

假如分配記憶體策略是指標碰撞,如果在高併發情況下,多個物件需要分配記憶體,如果不做處理,肯定會出現執行緒安全問題,導致一些物件分配不到空間等。

下面是解決方案:

3.3 執行緒同步策略

也就是每個執行緒都進行同步,防止出現執行緒安全。

3.4. 本地執行緒分配緩衝

也稱TLAB(Thread Local Allocation Buffer),在堆中為每一個執行緒分配一小塊獨立的記憶體,這樣以來就不存併發問題了,Java 層面與之對應的是 ThreadLocal 類的實現

4. 初始化

  1. 分配完記憶體後要對物件的頭(Object Header)進行初始化,這新資訊包括:該物件對應類的後設資料、該物件的GC代、物件的雜湊碼。
  2. 抽象資料型別預設初始化為null,基本資料型別為0,布林為false。。。

5. 呼叫物件的初始化方法

也就是執行構造方法。

二、物件的記憶體模型

頭資訊

在物件頭中有兩類資訊:標誌資訊(Mark Word)和型別指標(Kclass Pointer)
1. 標識資訊用來存放物件一些固有屬性的狀態,這些屬性從物件建立就有,而不是 Java 的使用者定義的:
* 雜湊碼:物件的唯一識別符號
* 物件的分代年齡:與垃圾回收有關
* 執行緒持有的鎖
* 鎖的狀態
* 偏向執行緒 ID、偏向時間戳
* 陣列長度:如果該物件是陣列,會有陣列長度資訊

  1. 型別指標是指向方法區中類元資訊的指標。
例項資訊(instanceData)

例項的資訊存放的是一些對 Java 使用者真正有效的資訊,也就是類中定義的各個欄位,其中還包括從父類繼承的欄位。hotspot把相同寬度的型別分配在一起。

記憶體的對齊填充(Padding)

對其填充這段記憶體段存在與否取決於前面兩部分的長度,為了保證物件記憶體模型的長度為 8 位元組的整數倍,這也是虛擬機器自動記憶體管理的要求(物件起始地址必須是8的整數倍)。

三、物件的訪問定位

物件建立起來之後,就會在虛擬機器棧中維護一個本地變數表,用於儲存基礎型別和基礎型別的值,引用型別與引用型別的值。
其中引用型別的值就是堆中物件地址。如何引用堆中地址有兩種方式:
* 控制程式碼:在堆中維護一個控制程式碼池,控制程式碼中包含了物件地址,當物件改變的時候,只需改變控制程式碼,不需要改變棧中本地變數表的引用
* 直接指標:物件的地址直接儲存在棧中,這樣做的好處就是訪問速度變快(Hotspot採用該方式)


相關文章