1. 程式人生 > >淺談對象的創建、內存布局和訪問定位

淺談對象的創建、內存布局和訪問定位

虛擬機創建 head 分配 完成 原子性 mar ava 失敗重試 檢查

在此簡單的記錄一下《深入理解Java虛擬機》第2章的2.3節內容。

對象的創建

  這裏的對象的創建是指普通的對象(不包括數組和Class對象)。對象的創建簡單來說就是執行new的時候,虛擬機做出對應的響應。讓我們看看一下虛擬機創建對象的過程:
1.虛擬機遇到new指令時,首先嘗試在常量池中定位到對應類的符號引用,並檢查這個符號引用代表類是否已被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程(後續會寫一下關於類加載的問題)。
2.類加載檢查通過後,為新生對象分配內存。對象內存的大小在類加載完成後便可完全確定。對象內存分配有“指針碰撞”和“空閑列表”兩種方法,“指針碰撞”是把已用內存放到指針的一邊,未用的放到另一邊,以指針分隔,當需要分配一個新對象內存時把指針往未分配內存那邊移動相對應的空間即可;“空閑列表”是因為內存已用的和未用的並不是規整的,它們是交錯的,所以需要一個列表記錄內存塊的情況。Java堆是線程之間共享的內存,虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性保證內存指針修改並發安全性;另一種方法是“本地線程分配緩沖(Thread Local Allocation Buffer TLAB)”。
3.將虛擬機分配到的內存空間初始化為零值。
4.對對象進行必要的設置。其實是對對象頭編寫。
5.完成上面4個步驟執行new指令後會接著執行

對象的內存布局

  對象在內存中存儲的布局可分為3部分:對像頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。

  • 對象頭
      對象頭包括兩部分信息:第一部分用於存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等;另一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。如果對象是一個Java數組,那麽對象頭還必須有一塊用於記錄數組長度的數據。因為虛擬機可以通過普通Java對象的元數據信息確定Java對象的大小,但是從數組的元數據中無法確定數組大小。
  • 實例數據
      實例數據部分是對象真正存儲的有效信息。
  • 對象填充
      因為自動內存管理系統要求對象的大小必須是8字節的整數倍,才有對象填充的說法。

對象的訪問定位

  Java程序需要通過棧上的reference數據來操作堆上的具體對象。reference類型存放的是對象的引用,但是具體怎麽樣訪問對象取決於虛擬機實現而定。目前主流的訪問方式有“使用句柄”和“直接指針”兩種。

淺談對象的創建、內存布局和訪問定位