一:oop-kclass模型
思考:我們平時寫的java類編譯成.class文件,JVM加載.class文件,那么加載.class文件之后在JVM中就是oop-kclass(C++)模型形式存在的。
JVM內部基於oop-klass模型描述一個java類以及其實例(對象),java類元信息用klass描述,對象用oop來描述。
oop: ordinary object pointer,也即普通對象指針.oop成員眾多
這里有個問題,不是說xxxoop嗎?怎么類圖上怎么就是xxxppDesc了?
原因在於c++描述oop的層級關系時在 oopsHierarchy.hpp 中進行了如下定義:

typedef class oopDesc* oop; typedef class instanceOopDesc* instanceOop; typedef class methodOopDesc* methodOop; typedef class constMethodOopDesc* constMethodOop; typedef class methodDataOopDesc* methodDataOop; typedef class arrayOopDesc* arrayOop; typedef class objArrayOopDesc* objArrayOop; typedef class typeArrayOopDesc* typeArrayOop; typedef class constantPoolOopDesc* constantPoolOop; typedef class constantPoolCacheOopDesc* constantPoolCacheOop; typedef class symbolOopDesc* symbolOop; typedef class klassOopDesc* klassOop; typedef class markOopDesc* markOop; typedef class compiledICHolderOopDesc* compiledICHolderOop;
其中各類的說明如下:
類名 | 說明 |
oop | oop的頂級父類 |
instanceOop | 表示java類實例 |
methodOop | 表示java方法 |
constMethodOop | 表示java方法中的只讀信息(其實就是字節碼指令) |
methodDataOop | 表示性能統計的相關數據 |
arrayOop | 數組對象,定義了數組oops的抽象基類 |
objArrayOop | 表示引用類型數組對象 |
typeArrayOop | 表示基本類型數組對象 |
constantPoolOop | 表示java字節碼文件中的常量池 |
constantPoolCacheOop | 與constantPoolOop 相伴生,是后者的緩存對象,緩存了字段和方法的訪問信息,為允許時環境快速訪問字段和方法提供重要作用 |
symbolOop | symbolOop是規范化字符串。所有symbolOop都位於全局符號表中。 |
klassOop | 指向jvm內部的klass實例的對象 |
markOop | oop 的標記對象,表示對象頭 |
compiledICHolderOop | 內聯緩存實現的幫助器對象。它包含從編譯到解釋調用轉換時使用的中間值(method+klass對),總是分配在永久區(以避免在清除期間遍歷codecache) |
上面的一個類就是描述我們平常創建的Java實例:instanceOopDesc,對象在內存中的布局就是通過它來表示的,關於這一點可以參考之前寫的關於
《關於Object=new Object();的美團六問》:https://blog.csdn.net/A7_A8_A9/article/details/105730007 的文章。
里面有個關於對象的內存圖:
總結起來就是:
instanceOopDesc包含如下信息:
1. 對象頭,也叫Mark Word,主要存儲對象運行時記錄信息,如hashcode, GC分代年齡,鎖狀態標志,線程ID,時間戳等;
2. 元數據指針class pointer,即指向方法區的instanceKlass實例 (虛擬機通過這個指針來群定這個對象是哪個類的實例。)
3. 實例數據 instance data;
4. 另外,如果是數組對象,還多了一個數組長度
5:對齊填充(有時候不需要)
klass
- klass 提供一個與java類對等的c++類型描述
- klass 提供虛擬機內部的函數分發機制
- jvm在加載class時,會創建instanceKlass,表示其元數據,包括常量池、字段、方法等,存放在方法區;instanceKlass是jvm中的數據結構;
在new一個對象時,jvm創建instanceOopDesc,來表示這個對象,存放在堆區,其引用,存放在棧區;它用來表示對象的實例信息,看起來像個指針實際上是藏在指針里的對象;instanceOopDesc對應java中的對象實例; - klass是在方法區的。
- 普通的Java類和數組用不同的kclass來表示,后面使用HSDB工具可以看到JVM運行時常量池中的Java實例存在的形式。
之間oop-klass之間的聯系:
handle
除了oop,kclass外在JVM中還有一個東西handle:
handle 是對oop的行為的封裝.這里需要注意的是:
- 大多數情況下,JVM在訪問java類時是一定通過handle的_handle 來得到oop,再通過oop獲得對應的klass,這樣,handle就能夠訪問oop的函數了.
- 如果是調用JVM內部的c++類所對應的oop函數,則不需要通過handle,直接通過oop拿到指定的klass即可.
三者的關系如下:
vtable: 該類所有的函數(除了static, final)和 父類的函數虛擬表。
Itable: 該類所有實現接口的函數列表.
二:使用HSDB查看JVM中運行時的內容
HSDB是JDK自帶的分析JVM的利器,運行方式在jdk安裝目錄/lib下有一個 sa-jdi.jar ,運行命令:java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
注意項目上用的jdk和環境配置的jdk要一致,不然就要指定上面那個java是哪個jdk版本的。
運行完會打開如下界面:
用的測試例子如下:已經在運行
我們查看這個進程的id:
得到id之后,操作如下
把剛才的id cp進去點擊ok
等待一會出現當前進程的線程棧:
選中main線程,點擊Tool下的class browser
看到當前線程所涉及到的類,
@后面是地址,我們cp下TestLog的地址:
填入下面的位置回車:我們就可以看到在JVM中我們定義的TestLog其實是用InstanceKlass來表示的。
我們看到TestLog的Class在JVM中也是用Oop表示的。
1:HotSpot並不把instanceKlass暴露給Java,而會另外創建對應的instanceOopDesc來表示java.lang.Class對象,並將后者稱為前者的“Java鏡像”就是圖中看到的'_java_mirror'屬性。
2:InstanceKlass持有指向oop引用(_java_mirror便是該instanceKlass對Class對象的引用)。
3:new操作返回的是對instanceOopDesc類型的引用(在棧中),而instanceOopDesc指針(堆中)指向instanceKlass(方法區),而instanceKlass指向了對應的類型的Class實例的instanceOopDesc(就是那個_java_mirror)