Java虛擬機專題對象內存定位

一  對象在內存中的佈局

1.1對象的創建過程

對象的創建過程可以如下圖所示:


1.2 什麼是符號引用和直接引用,爲什麼需要在常量池定位到符號的引號?

在類的解析階段,把虛擬機常量池內的符號引號替換爲直接引用。

1.2.1 符號引用(SymbolicReferences)

就是用一組符號來描述所引用的目標,符號可以是任何形式的,只要使用時能夠定位到目標即可。

我們知道Java類編譯成class文件,編譯的時候,該類並不知道其所引用類的實際內存地址,因此只能使用符號引用來代替。

 

1.2.2 直接引用

直接飲用就是直接指向目標的指針,比如指向對象,或者類方法,類變量的指針,亦或者指向實例變量和實例方法的指針

 

二 對象內存分配方式

2.1 指針碰撞

指針碰撞就是指需要分配內存的時候,只需要將指針向空閒區域移動即可。所以堆內存空間空閒內存需要十分規整纔可以。

 

2.2 空閒列表

很多時候,正在使用的內存和未被使用的內存十分不規整,分佈很零散。虛擬機維護了一個空閒列表,記錄哪些內存塊可用,每次要分配內存時候就去空閒啊列表查詢,找到一個足夠的內存塊去分配。

我們比較這兩者分配方式,很明顯指針碰撞的分配效率高於空閒列表,但是前提是空閒內存規整。如果不規整的話,空閒列表是比較適合的,比指針碰撞節約內存。

 

三 內存分配線程安全問題

假設我們線程A請求分配內存,虛擬機從空閒列表查詢取出了一塊內存,分配給線程A的對象,這時候還沒來得及更新空閒列表,線程B也請求分配內存,也把這塊內存分配給了線程B,這就存在內存分配線程安全問題。

怎麼解決呢?

第一種方式:加鎖(影響性能)

第二種方式:TLAB (ThreadLocal Allocate Buffer)本地線程分配緩衝區

 

四 對象結構

# Header: 主要包括自身運行時數據;類型指針

# InstanceData

# Padding

4.1 對象頭(header)

對象頭用於存儲一些元數據,比如自身運行時數據和類型指針。

自身運行時數據(Mark Word)

# 哈希值

# GC分代年齡

# 鎖狀態標誌

# 線程持有的鎖

# 偏向線程ID和時間戳

它佔用內存大小和操作系統有關,如果是32位的操作系統,那麼它就是32位;如果是64位的操作系統,那麼他就是64位。如果數據遠遠大於32位,是如何進行存儲的呢?



類型指針

指向對象指向其類的元數據的指針,虛擬機通過指針來確定對象是哪一個類的實例。

 

4.2 實例數據(InstanceData)

4.3 對齊填充(Padding)

爲什麼需要填充?

對齊填充並不是必然的,也沒什麼特殊的含義,僅僅起着佔位符的作用。HotSpot虛擬機內存管理規定對象起始地址必須是8字節的整數倍,比如header部分正好都是8的整數倍,如果實例數據部分大小不夠8的整數倍,即沒有對齊,就需要使用Padding填充

 

五 對象的訪問定位

棧中的reference引用指向堆內存的地址並不一定就是對象本身。

5.1 使用句柄

並不是直接指向,而是指向堆中一塊區域,叫做句柄池。句柄池裏保存了實例對象的地址。

優點:棧中reference的引用地址不需要改變,改變的只是句柄池的對象實例地址

5.2 直接指針

直接指向堆中對象內存區域,HotSpot就是使用這種



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章