前面介紹了jvm運行時數據區域后,下面講解下對內存中數據的其他細節,看他們是如何創建、布局及訪問的
一、對象的創建
1.對象的分配
對象的創建分配方式主要有兩種:指針碰撞和空閑列表
指針碰撞:
假設堆內存中是絕對規整的,那么,在為新對象分配內存空間時,只需要將指針向空閑空間方向移動新對象
所需大小的一段出來即可。
一般使用帶有compact(整理)過程的收集器時,使用指針碰撞
空閑列表:
如果內存不是規整的,這時就需要維護一個列表,記錄哪些內存是空閑的,在分配空間時,從列表中找出一塊
足夠大的空間划分為對象實例並更新列表記錄
使用基於mark-sweep算法的收集器時,使用空閑列表
2.對象的初始化及設置
初始化:內存分配完成后就是對對象進行初始化,虛擬機將初始化的內存空間都初始化為0值,
這就是為什么對象的實例字段在java代碼中不需要初始化也可以使用
設置:之后,虛擬機對對象進行一些必要設置,主要是往對象頭中存入類的元數據信息、對象的哈希值、分代年齡等。
init: 經過上面兩步后對象創建完成,但所有字段還都是0值。最后就是執行init方法,按程序員的意思初始化對象
完成上述三步后,一個對象就算創建完成。
二、對象的內存布局
對象在內存中存儲的布局主要分為三部分,對象頭、實例數據、對齊填充
對象頭:
對象頭在對象設置階段提到過,即在設置階段會向對象頭中存入一些類的元數據信息、哈希值、分代年齡等
對象中主要分為兩部分:運行時數據和類型指針
運行時數據:主要存儲上面對象設置階段存儲的哈希碼、分代信息、鎖狀態標識、線程持有的鎖等。
類型指針:主要存儲對象的類元數據的指針,即這個對象是哪個類的實例,此外,如果對象類型是數組,
對象頭中還會存儲一個代表數據長度的數據
對齊填充:不是必然存在的,在Hotspot中,要求對象起始地址必須是8字節的倍數,
當對象實例部分沒對齊時,用對齊填充來補全。
三、對象的訪問定位
java程序是通過棧上的reference數據來操作對象實例的,因此它只是一個引用,具體定位方式jvm有不同實現
主流實現方式有兩種:使用句柄和直接指針
句柄池:
原理:java堆中會划出一片區域作為句柄池,reference存儲的就是句柄地址,
里面包含對象實例數據的指針和對象類型指針
好處:如果對象被移動,只需改變句柄池中存儲的地址
直接指針
原理:如果使用的是直接指針,那么reference中存儲的就是對象的地址
好處:省去了一次定位的時間,效率更高
Hotspot使用的是直接指針方式
