前言:
Oop-Klass體系回顧,在JVM第一篇中講過了這部分內容,今天的內容也涉及,回顧一下。
ooPDesc
---------MarkOopDesc:存放鎖的信息,分代年齡等等
---------InstanceOopDesc:非數組對象
---------arrayOopDesc:數組對象
-----typeArrayOopDesc:基本數據類型數組,對應的有個存放基本數據類型數組元信息的TypeArrayKlass。
-----objArrayOopDesc:引用數據類型數組,對應有個objArrayKlass存放引用類型的元信息。
我們這里再舉個例子,通過HSDB查看在JVM中的對象。
啟動HSDB,在jdk1.8/lib下執行:java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
選擇main線程,打開stack memroy
第一個就是我們定義的Position對象的地址,第二個是字節數組,第三個是char數組。
我們Inspector查看Position對象。第一部分就是Oop,在對象里面有元數據類型的InstanceKlass。
這里有個重要的點就是:_layout_helper參數。
1:如果是非數組對象,這個大小指的就是類生成的對象的大小。
2:如果是數組對象,是個負值。
3:等於0的話,?
內存布局
我之前寫過一篇博客里面提到了這部分內容:https://blog.csdn.net/A7_A8_A9/article/details/105730007?spm=1001.2014.3001.5501
補充:針對上面的內存布局,這里有幾點需要說明。
1:對象頭中類型指針-class pointer它是指向instanceklass在方法區的地址,如果開啟指針壓縮的情況下是占4B,如果不開啟是占8B。
2:如果對象不是數組,對象頭中的數組長度占0B。是數組的話占4字節,因此數組的長度最大為2的32次方-1.
3:實例數據中char類型數據在c++中是用short表示的,所以占兩個字節。
4:實例數據中的引用數據類型,如果開啟指針壓縮占4B,不開啟指針壓縮占8B。
計算對象大小
指針壓縮從jdk1.6之后是默認開啟的。可以通過參數控制。
-XX:+UseCompressedOops/-XX:-UseCompressedOops
可以引用:這個依賴來打印對象布局大小。
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency>
1:沒有實例數據的對象
測試代碼:
public class TestClass { public static void main(String[] args) { TestClass tes1= new TestClass(); System.out.println(ClassLayout.parseInstance(tes1).toPrintable()); } }
結果:一共占16字節。
我們算下,這個16是怎么來的。其實很簡單就是:對象頭8B+類型指針(壓縮)4B+數組長度0B+實例數據0B+4B(對齊填充)=16B
不開啟指針壓縮就是:對象頭8B+類型指針(不開啟指針壓縮)8B+數組長度0B+實例數據0B=16B 沒有了對齊填充也是16B
2:有實例數據
我們給TestClass加兩個實例屬性。
public class TestClass { int i=2; String name="阿三"; public static void main(String[] args) { TestClass tes1= new TestClass(); System.out.println(ClassLayout.parseInstance(tes1).toPrintable()); } }
開啟指針壓縮:
Mark Word 8B+類型指針4B(壓縮)+數組長度0B+實例數據int 4B+引用數據類型4B(壓縮)+4B(對齊填充)=24B
我們看下運行結果:
關閉指針壓縮:
對象頭8B+類型指針8B(壓縮)+數組長度0B+實例數據int 4B+引用數據類型8B(壓縮)+4B(對齊填充)=32B
運行結果:
指針壓縮
開啟指針壓縮是為了節省內存,尋址效率有些提高。
指針壓縮的原理:
假如分配的內存從0開始且順序存儲,三個對象分別: test1=16B test2=24B test3=32B, 三個地址分別:test1=00000, test2=16(十進制) /10000(二進制),test3=40(十進制) 101000(二進制)。
我們都知道java中的對象都是8字節對齊的,8字節對齊有一個特點就是 1 000,發現了嗎 所有對象的指針后三位總是0。這就是指針壓縮的點。
壓縮原理就是兩句話:
1:存儲的時候,后三位抹除0.
就變成:test1=00,test2=10
2:使用的時候,后三位補0.
它的指針不再表示對象在內存中的精確位置,而是表示 偏移量 。這意味着 32 位的指針可以引用 40 億個 對象 , 而不是 40 億個字節。最終, 也就是說堆內存增長到 32 GB 的物理內存,也可以用 32 位的指針表示。
使用HSDB查看指針壓縮現象:
示例代碼:
public class TestClass { public static void main(String[] args) { Position position = new Position(1, 2, 3); while(true); } }
關閉指針壓縮:
對象地址:
打開指針壓縮:
對象大小明顯變小了,而且klass屬性也不一樣了。
這是因為在OopDesc中,指針壓縮和不壓縮klass存儲在不同的地方。
JVM調優基礎知識
1:上線前對JVM進行預估調優
2:上線后小規模調優
3:OOM,full GC 頻繁調優
主要調什么?
1:方法區
2:虛擬機棧
3:堆區
4:熱點代碼緩沖區