JVM中一個小知識點:深堆和淺堆

java中的堆內存算是整個內存區域中最重要的一塊,幾乎所有的對象都分配在堆內存。在堆內存中有兩個主要的概念需要我們理解,這對分析java堆內存的故障有著重要的作用,分別是深堆和淺堆。

一、概念

我們先給出概念,然後再分析。

(1)淺堆表示一個java對象結構所佔用內存的大小,這個java對象內部包含了三部分數據:對象頭、實例數據和對齊填充。

(2)深堆表示一個java對象被 GC 回收後,可以真實釋放的內存大小。

如果你有點疑惑,沒關係。對上面出現的那些名詞我們一個一個來解釋。

1、對象頭

一個對象分為三部分:對象頭、實例數據、對齊填充。對象頭又包含了兩部分:標記部分和原始對象引用。

(1)標記部分:包括 hashcode、gc 分代年齡、鎖狀態標誌、線程持有鎖、偏向線程鎖id,偏向時間戳,這一部分在32位機器上為 4 byte,64 位機器上為 8byte,一般來說我們的機器都是64位,所以現在默認指的是8個字節。

(2)原始對象引用是對象的指針,通過這個指針找到對象的實例,該數據可以壓縮,32 位機器上為 4 byte,64位機器上為 8byte,jdk8默認開啟壓縮,大小為 4byte。

所以我們可以看到:一個java對象的對象頭大小,沒壓縮的時候是16字節,壓縮時候是12個字節。

2、實例數據

指的就是我們的java對象實例。這個很容易理解。

3、對齊填充

對齊填充要追根其根源,其實是到了計算機系統結構的學科。我在大學的時候學到了對齊填充,和這裡是一樣的道理。java中的對象都是以8個字節為單位對齊,所以每一個java對象的大小都是8的整數倍。

4、保留集

對象A能直接或者是間接訪問其他的對象,這些對象集合起來就是A的保留集。當進行垃圾回收的時候,不僅回收A,也會回收保留集中所有的對象。

現在我們理解了這些基本的概念,再回過頭來好好地看看,深堆和淺堆到底是什麼?

淺堆指對象本身佔用的內存,不包括其內部引用對象的大小。一個對象的深堆指只能通過該對象訪問到的(直接或間接)所有對象的淺堆之和,即對象被回收後,可以釋放的真實空間。

在《實戰java虛擬機》中,使用了一個案例,在這裡引用一下:

JVM中一個小知識點:深堆和淺堆

A引用了對象C,D,對象B引用了對象C,E。

對象A引用了C和D,對象B引用了C和E。那麼對象A的淺堆大小隻是A本身,不含C和D,而A的實際大小為A、C、D三者之和。

而A的深堆大小為A與D之和,由於對象C還可以通過對象B訪問到,因此不在對象A的深堆範圍內。

我們再舉一個例子來分析:1到100,被3整除的數可以送給對象A,被5整除的可以送給對象B。那麼在程序退出時候,進行垃圾回收,此時對象A的的深堆肯定小於33,對象B的深堆肯定小於20,這是因為存在像15、30這樣的數,既可以被3整除也可以被5整除,屬於三不管對象。

代碼就不再演示了,最後的結果還是使用MAT工具進行分析一下。


分享到:


相關文章: