java對象內存模型
對象頭:Instance Header,存儲了Java對象hash、GC年齡、鎖標記、class指針、數組長度等信息。在64位系統中,其中mark占8字節,klass指針區在打開指針壓縮時,占4字節,關閉指針壓縮時占8字節。指針壓縮開關默認打開,通過-XX:-UseCompressedOops控制關閉。
整個對象的大小,由header(對象頭),body(數據),padding(數據補齊)3個部分組成,VM要求對象大小須是8的整體數,該部分是為了讓整體對象在內存中的地址空間大小達到8的整數倍而額外占用的字節數。
【length】存的內容
可以通過以下例子直觀的感受每個對象的大小和數據補齊,指針壓縮的的效果。
當vm參數中關閉指針壓縮時,8個基本類型的對象大小如下
當vm參數中打開指針壓縮時
通過以上例子,我們了解了一個基本類型對象的大小,和是否打開指針壓縮的區別。但在我們實際編碼中,使用最多的對象往往不是基本類型,而是String.
那么一個String對象又有多大呢?我們繼續看以下例子(下面的截圖均是打開了指針壓縮的運行結果)
從這個例子可以看出來,一個空String的大小就有40字節,而隨著String里字符串內容的增加,所占用的內存也會增加,大概是每增加一個字符,增加2字節,原因其實也很好解釋,因為String的主要數據存儲就是一個char數據,每增加一個字符,就相當于增加了一個char字符,也就是2字節。
接著我們來看我們實際場景中一個常用對象呢
可以看出對象的大小即是自己本身大小,加上里面屬性的大?。ㄈ绻麑傩灾禐榭找策€會有一個引用大小4,從空的testModel1和testModel2的大小可以看出)。那我們現在來估算一下,
1個list里面有10個TestModel2對象,每個TestModel2對象有10個String對象,每個String對象長度為4.這樣一個list會占用多少內存?
單個TestModel對象,(24+40+4)10+8+4+4(對齊補充)= 536字節
10個TestModel就會是5360加上list對象中的其他屬性應該是略大于5360的。(注意如果屬性之間的值有很多相等的,這個大小會大幅減少,應該會復用同一個引用地址)
相信通過這之前的描述,我們已經對一個對象占用多大的空間有了一個大概的概念,那么在實際開發中了解這些又有什么用呢。
1.在查詢整張表的數據時,如果表中數據較大,應該多次查詢。
2.在使用線程池時,使用隊列時,盡可能指明隊列長度,一個無界的隊列很容易把jvm打爆。
接下來我們來模擬下,在-Xmx256M 的環境下一個多大的list會把內存打爆。
可以發現一個10個屬性的簡單對象,只需要20w不到的數據量就可以把256m的內存打爆,發生oom,而且這還是在沒有其他并發的邏輯的情況下,真實的生產環境,能支撐的數據量更少。
所以,在我們平常代碼中,還是需要注意全量查詢和隊列的場景,都是oom的高發點。
第一部分到這里也就結束了。這里遺留了一個問題,在195000個對象后,整個list的大小大概只有122m,為啥會把256的堆內存打爆?下一節我們會揭開這個問題的答案。