Pre
JVM - 剖析Java對象頭Object Header之對象大小
mark word : 32位 佔4字節 ,64位 佔 8字節
klass point : 開啓壓縮佔4字節,未開啓 佔 8字節。
指針壓縮
論證壓縮效果
- jdk1.6 update14開始,在64bit操作系統中,JVM支持指針壓縮
- 啓用指針壓縮
-XX:+UseCompressedOops
(默認開啓),禁止指針壓縮:-XX:-UseCompressedOops
oop(ordinary object pointer) 就是對象指針的意思。
運行參數增加
-XX:-UseCompressedOops
禁用指針壓縮,我們來看下對象頭的大小
package com.gof.test;
import org.openjdk.jol.info.ClassLayout;
/**
* @author 小工匠
* @version v1.0
* @create 2020-06-25 16:21
* @motto show me the code ,change the word
* @blog https://artisan.blog.csdn.net/
* @description
**/
public class ObjectHeaderTest {
public static void main(String[] args) {
ClassLayout layout = ClassLayout.parseInstance(new Object());
System.out.println(layout.toPrintable());
System.out.println();
ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
System.out.println(layout1.toPrintable());
System.out.println();
ClassLayout layout2 = ClassLayout.parseInstance(new ArtisanTest());
System.out.println(layout2.toPrintable());
}
// -XX:+UseCompressedOops 默認開啓的壓縮所有指針
// -XX:+UseCompressedClassPointers 默認開啓的壓縮對象頭裏的類型指針Klass Pointer
// Oops : Ordinary Object Pointers
public static class ArtisanTest {
//8B mark word
//4B Klass Pointer 如果關閉壓縮-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops,則佔用8B
int id; //4B
String name; //4B 如果關閉壓縮-XX:-UseCompressedOops,則佔用8B
byte b; //1B
Object o; //4B 如果關閉壓縮-XX:-UseCompressedOops,則佔用8B
}
}
【輸出結果】
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 00 1c e4 17 (00000000 00011100 11100100 00010111) (400825344)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
[I object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 68 0b e4 17 (01101000 00001011 11100100 00010111) (400821096)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
20 4 (alignment/padding gap)
24 0 int [I.<elements> N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
com.gof.test.ObjectHeaderTest$ArtisanTest object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) f8 a5 4e 18 (11111000 10100101 01001110 00011000) (407807480)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 4 int ArtisanTest.id 0
20 1 byte ArtisanTest.b 0
21 3 (alignment/padding gap)
24 8 java.lang.String ArtisanTest.name null
32 8 java.lang.Object ArtisanTest.o null
Instance size: 40 bytes
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total
我們先把默認的開啓指針壓縮的這個的測試結果題圖,方便比對
【默認開啓指針壓縮】
VS
【關閉指針壓縮】
【默認開啓指針壓縮】
VS
【關閉指針壓縮】
最後一個,對於包含多個變量的對象的對象頭
【默認開啓指針壓縮】
VS
【關閉指針壓縮】
UseCompressedOops & UseCompressedClassPointers
-XX:+UseCompressedOops 默認開啓的壓縮所有指針
-XX:+UseCompressedClassPointers 默認開啓的壓縮對象頭裏的類型指針Klass Pointer
【指針壓縮】開啓 VS 關閉
類型 | 開啓指針壓縮 | 關閉指針壓縮 |
---|---|---|
Object | 16 | 16 |
int數組 | 16 | 24 |
ArtisanTest對象 | 32 | 40 |
指針壓縮的目的
同一個對象, 不開啓指針壓縮 8字節 存入堆中和 開啓指針壓縮4字節存入堆中,哪個更好一些,顯而易見。
簡言之:爲了更好地節省內存,避免GC壓力過大。
同時在64位平臺的HotSpot中使用32位指針(實際存儲用64位),內存使用會多出1.5倍左右,使用較大指針在主內存和緩存之間移動數據,佔用較大寬帶。
所以爲了減少64位平臺下內存的消耗,JVM在1.6以後默認啓用指針壓縮功能。
爲什麼堆內存最好不要超過32G
在jvm中,32位地址最大支持4G內存(2的32次方) 。
我們知道以前32位的操作系統 ,內存格中最多存放32位 ,所以
2的32次方 等於4294967296 字節 = 4 G 。
64位,不是2的64次方,這個值簡直太大了。。。。
在jvm中,32位地址最大支持4G內存(2的32次方),可以通過對對象指針的存入堆內存時壓縮編碼、取出到cpu寄存器後解碼方式進行優化,
舉個例子 對象指針在堆中是32位,在寄存器中是35位,2的35次方=32G),使得jvm只用32位地址就可以支持更大的內存配置(小於等於32G) 。
JVM如何處理的?
-
當堆內存小於4G時,不需要啓用指針壓縮,jvm會直接去除高32位地址,即使用低虛擬地址空間
-
當堆內存大於32G時,壓縮指針會失效,會強制使用64位(即8字節)來對java對象尋址, 那這樣的話內存佔用較大,GC壓力等等