聊聊高并发(四)Java对象的表示模型和运行时内存表示
在繼續了解Java內存模型之前,最好先理解Java對象的內存表示。在網上搜了下Java對象內存表示,說得都不夠系統和到位。之前看了《Hotspot實戰》一書,對JVM如何表示對象這塊說得挺好,推薦一下。如果不理解JVM運行時的各種內存區域以及Java調用的過程,那么很難把Java內存模型理解到位。這個是一個比較大的主題,以后會陸續寫一些JVM相關的。這里單把Java對象的內存拿出來聊聊,文中內容都基于Hotspot虛擬機。
?
Hotspot主要是用C++寫的,所以它定義的Java對象表示模型也是基于C++實現的。
Java對象的表示模型叫做“OOP-Klass”二分模型,包括兩部分:
1. OOP,即Ordinary Object Point,普通對象指針。說實話這個名稱挺難理解。說白了其實就是表示對象的實例信息
2. Klass,即Java類的C++對等體,用來描述Java類,包含了元數據和方法信息等
?
一個Java對象就包括兩部分,數據和方法,分別對應到OOP和Klass。最簡單的理解就是如果讓你自己用Java語言來開發一套新的語言,你如何來表示這個新的語言的對象呢。肯定也是類似的思路,一個模塊是用Java類來實現表示數據的部分,一個模塊是用Java類實現表示方法和元數據的部分。
?
JVM運行時加載一個Class時,會在JVM內部創建一個instanceKlass對象,表示這個類的運行時元數據。創建一個這個Class的Java對象時,會在JVM內部相應的創建一個instanceOop來表示這個Java對象。熟悉JVM的同學可以明白,instanceKlass對象放在了方法區,instanceOop放在了堆,instanceOop的引用放在了JVM棧。
?
JVM是基于棧來運行的,當一個線程調用一個對象的方法時,會在它的JVM棧的棧頂創建一個棧幀(Frame)的數據結構,這個數據結構是用來保存方法的局部變量,操作數棧,動態連接和方法返回值的。通過參數傳遞的值和在方法中new出來的對象的引用都保持在局部變量表里面。
Java的方法調用是值傳遞,不是引用傳遞,原因就在這里,傳遞進來的參數相當于在局部變量表里面拷貝了一份,實際計算時,操作數棧操作的是局部變量變量里面的值,而不是外部的變量。
?
在堆中創建的Java對象實際只包含數據信息,它包含三部分:
1. 對象頭,也叫Mark Word
2. 元數據指針,可以理解為類對象指針,指向方法區的instanceKlass實例
3. 實例數據
如果是數據對象的話,還多了一個部分,就是數組長度。
對象頭主要存儲對象運行時記錄信息,如hashcode, GC分代年齡,鎖狀態標志,偏向線程ID,偏向時間戳等。對象頭的長度和JVM的字長一致,比如32位JVM的對象頭是32位,64位JVM的對象頭是64位。
這里可以看到,所謂的給一個對象加鎖,其實就是設置了對象頭某些位。當其他線程看到這個對象的狀態是加鎖狀態后,就等待釋放鎖。
?
在方法區的instanceKlass對象相當于Class加載后創建的運行時對象,它包含了運行時常量池,字段,方法等元數據,當調用一個對象的方法時,如上面的圖所示,實際定位到了方法區的instanceKlass對象的方法元數據。
?
下面我們通過一個實例,使用HSDB來看看運行時的instanceKlass和instanceOop到底是什么樣的。在方法區
?
創建一個Person類,有name, age, sex實例屬性,有一個sayHi方法
?
?package main;
public class Person {
?? ?private String name;
?? ?private int age;
?? ?private boolean sex;
?? ?
?? ?public void sayHi(){
?? ??? ?System.out.println("Say hi from ITer_ZC");
?? ?}
?? ?
?? ?public static void main(String[] args){
?? ??? ?Person p = new Person();
?? ??? ?p.sayHi();
?? ??? ?
?? ??? ?try {
?? ??? ??? ?Thread.sleep(500000);
?? ??? ?} catch (InterruptedException e) {
?? ??? ??? ?e.printStackTrace();
?? ??? ?}
?? ?}
}
HSDB是一款內置與SA的GUI調試工具,集成了各種JVM監控工具,可以用來深入分析JVM內部狀態。
?
啟動HSDB:
?
java -cp ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
運行Person,使用JPS查看進程ID
?
?
使用HSDB attach Person進程
?
Attach成功后看到21461進程里的各個子進程
?
在Class Browser里面找到Person對應的instanceKlass的運行時實例
?
在inspector里面查看0x000000077fc82328這個對象實例,我們可以看到instanceKlass的字段,方法,運行時常量池,父類,兄弟類等元數據信息
?
在Object Histogram里面找到main.Person對象
?
查看main.Person Oop的運行時實例
?
_mark就是對象頭,接著是實例數據信息。HSDB沒有顯示類對象指針
總結
以上是生活随笔為你收集整理的聊聊高并发(四)Java对象的表示模型和运行时内存表示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊高并发(二)结合实例说说线程封闭和背
- 下一篇: 聊聊高并发(五)理解缓存一致性协议以及对