背景摘要:前兩篇文章主要提到過JVM內存結構、JVM對象創建過程及空間開闢。JVM內存結構篇中我們瞭解到了對象一般是存放於堆中,那麼其實棧也是可以存放對象的,這就是基於我們的JVM棧上分配了。至於JVM對象創建篇,我們瞭解了對象的創建詳細過程步驟,以及JVM是如何爲對象分配空間,那麼這裏也繼續分析JVM的對象組成部分以及對象引用方式。
目錄
一、JVM棧上分配
我們在之前瞭解過對象一般存放於堆中,那麼其實棧也是可以存放對象的,但是一般都是棧幀裏面的對象,也就是不會給外部方法共享使用,內部使用後就結束。在JDK 1.6.25之後,可以進行棧上分配。也就是這個原理。
1.1、棧上分配
如果被調用方法的new對象,是棧幀(即方法)中所私有的,無需共享,那麼就無需存放在堆中,可存入棧。即棧上分配。
1.2、內存逃逸
比如此處僞代碼,理論上會執行棧上分配的new對象,但需要返回給外部方法使用,就不能存在於棧幀中,就只能放入堆中共享,這種行爲就叫做內存逃逸。
public Object a(){
return new Object();
}
public static void main(String[] args){
Object o = a();
o.toString();
}
注:重點在於其他地方是否需要引用。如果被使用絕對不能被棧幀私有化,只能分配在堆中,所以發生內存逃逸。
當然如果對象太大,也只能在堆上分配。
一句話:理論上應被棧上分配的對象,由於外部引用或對象太大無法執行,從而分配在堆中,即內存逃逸。
PS:來自逃出來的對象:
二、JVM對象組成部分
JVM對象包含對象頭、對象實例數據、數據填充。
當然此對象非彼對象
1.對象頭
哈希值: gc分代年齡、鎖狀態標誌、線程持有的鎖、線程當前ID
類型指針:當前對象指向那個class對象,對應調用的方法也是調用class對象中的方法
數組長度:只有數組纔會有這個值
2.對象實例數據
主要存放自身的 屬性變量,包括父類屬性等。
3.數據填充
使用數據填充,沒有實際的意義 HotStop 虛擬機指定對象大小必須是8個字節的整數倍。如果不是8個字節則使用此進行填充
三、對象引用方式
棧裏面的變量可以引用到堆裏的對象,至於怎麼去引用的,Java沒說。
但是其實就是有兩種方法引用:
1、直接引用 速度較快,但是對象改變的時候,棧的指向也要改變
2、句柄引用 速度慢,有兩層,但是對象改變時,棧的指向無需改變,堆內改變引用即可。
不同的虛擬機不一樣,oracle是直接引用。都是虛擬機自動操作。
原理圖如下:
總結
JVM棧上分配與內存逃逸:
在方法體內的變量理論上應被棧上分配的對象由於外部引用或對象太大無法執行,從而分配在堆中,即內存逃逸。
對象組成部分:
首先是對象頭。哈希值,存放對象當前線程信息,類型指針(當前對象指向那個class對象)等,並且數組包含長度值。
對象實例包含自身屬性變量與父類屬性、最後數據填充是爲了虛擬機機制而準備,必須是8的整數倍。
對象引用方式:
直接引用效率高,但對象改變時需要更新引用地址。句柄引用效率較低,但對象改變無需重定向,堆內部處理即可。
注:堆的值更新是因爲垃圾回收算法中的複製算法。把當前空間的對象刪掉在其他空間創建。固更改了對象地址。