OOP-Klass Model(Ordinary Object Point-Klass Model)指的是普通對象指針,用來描述 java 類和對象在 JVM 中的表現形式,OOP 用來表示 java 實例在 JVM 中的表現,Klass 用來表示類在 JVM 中的表現。之因此要一分爲二的設計是由於想要避免每一個 Java 對象都存在一個虛函數,因此 oop 實例沒有虛函數,而 Klass 類有虛函數,虛函數則是實現多態的關鍵因此 Java 最終也是經過虛函數來實現多態的,下面來分別深刻了解下。java
本文基於 jdk1.8 hotspot, hostspot源碼下載地址c++
OOP(Ordinary Object Point)普通對象指針,它的定義以下,提取了須要講解的部分關鍵代碼編程
class oopDesc {
private:
// mark word 相關信息
volatile markOop _mark;
// 元數據
union _metadata {
// 實例對應的 Klass (實例對應的類)的指針
Klass* _klass;
// 壓縮指針
narrowKlass _compressed_klass;
} _metadata;
private:
// field addresses in oop
// 私有的讀取實例數據的方法實如今 oop.inline.hpp 中
void* field_base(int offset) const;
jbyte* byte_field_addr(int offset) const;
jchar* char_field_addr(int offset) const;
jboolean* bool_field_addr(int offset) const;
jint* int_field_addr(int offset) const;
jshort* short_field_addr(int offset) const;
jlong* long_field_addr(int offset) const;
jfloat* float_field_addr(int offset) const;
jdouble* double_field_addr(int offset) const;
Metadata** metadata_field_addr(int offset) const;
// 公有的讀取或者寫入實例數據的方法
public:
void byte_field_put(int offset, jbyte contents);
jchar char_field(int offset) const;
void char_field_put(int offset, jchar contents);
jboolean bool_field(int offset) const;
void bool_field_put(int offset, jboolean contents);
jint int_field(int offset) const;
void int_field_put(int offset, jint contents);
};
複製代碼
由此咱們能夠看到 oop 主要包括了 3 部份內容 數組
咱們將下面的分紅 3 塊內容來說mark 也就是 mark word 是對象頭的一部分在 markOop.hpp 中它含有哪些內容呢,源碼註釋定義以下多線程
爲了方便你們觀看,盜圖 2 張(來自併發編程藝術)結合源碼註釋能夠看到,第一張圖是 32 位虛擬機下 Mark Word 的表示,圖 2 是 64 位 Mark Word 的表示併發
元數據指針,它包含了 2 部份內容,klass 和 _compressed_klass 他們指向了對象所屬的類,具體的在 Klass 中講解jvm
查找和插入實例數據方法,舉一個列子來看,其它的都是相似的函數
jint int_field(int offset) const;
void int_field_put(int offset, jint contents);
複製代碼
實如今 oop.inline.hpp 中oop
inline jint oopDesc::int_field(int offset) const {
return *int_field_addr(offset);
}
inline void oopDesc::int_field_put(int offset, jint contents) {
*int_field_addr(offset) = contents;
}
inline jint* oopDesc::int_field_addr(int offset) const { \
return (jint*) field_base(offset);
}
inline void* oopDesc::field_base(int offset) const {
return (void*)&((char*)this)[offset];
}
複製代碼
能夠看到上面的方法能夠根據傳入的 offset 在內存中,查找或者插入指定的數據而且返回其內存地址。post
由上面這些內容能夠看出,oop 其實就是描述了類的實例,包含了對象頭,實例數據等,而因爲在 java 中咱們的對象類型有不少種,好比數組對象,自定義對象等等同時也對應了不一樣的 oop,部分數據以下
// 各類 oop 的基類
typedef class oopDesc* oop;
// 表示一個 java 實例
typedef class instanceOopDesc* instanceOop;
// 表示一個數組實例
typedef class arrayOopDesc* arrayOop;
// 表示一個對象數組實例
typedef class objArrayOopDesc* objArrayOop;
// 表示一個容納基本類型的數組
typedef class typeArrayOopDesc* typeArrayOop;
複製代碼
上面這些 oop 都是繼承與 oopDesc 在 jdk1.7 hotspot 中沒有其它的操做,就只是一個空繼承,在 jdk1.8 hotspot,有增長一些方法,咱們挑選 1 個來看看,感興趣的能夠去看看源碼
class instanceOopDesc : public oopDesc {
public:
// aligned header size.
static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }
// If compressed, the offset of the fields of the instance may not be aligned.
static int base_offset_in_bytes() {
// offset computation code breaks if UseCompressedClassPointers
// only is true
return (UseCompressedOops && UseCompressedClassPointers) ?
klass_gap_offset_in_bytes() :
sizeof(instanceOopDesc);
}
static bool contains_field_offset(int offset, int nonstatic_field_size) {
int base_in_bytes = base_offset_in_bytes();
return (offset >= base_in_bytes &&
(offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
}
};
複製代碼
因此,當咱們使用 new 建立一個自定義的 Java 對象的時候,會建立一個 instanceOopDesc 來表示這個實列,當使用 new 建立一個數組時候會建立一個 objArrayOopDesc 來表示數組這個實列...
講 oop 的時候咱們說過,它有一個元數據指針指向它所屬的類,可以想到,咱們建立對象的時候有時候是有一些常量,方法等信息會保存在元數據中,元數據的定義以下
// The metadata hierarchy is separate from the oop hierarchy
// class MetaspaceObj
// 這是關於 oop 的一些信息
// 表示一個 java 方法中不變的信息包括方法名、方法的訪問修飾符、字節碼、行號表、局部變量表等等
class ConstMethod;
// 主要用於存儲某些字節碼指令所需的解析(resolve)好的常量項,
// 例如給[get|put]static、[get|put]field、
// invoke[static|special|virtual|interface|dynamic]等指令對應的常量池項用
class ConstantPoolCache;
// 記錄了 Java 方法執行時候的性能相關的 profile 信息,包括條件跳轉是否老是走一個分支
// 某處判斷從不爲 null 等信息
class MethodData;
// class Metadata
class Method;
// class 文件中的常量池
class ConstantPool;
// class CHeapObj
class CompiledICHolder;
複製代碼
能夠看到 MetaspaceObj 分類下主要是用於描述 oop 相關的信息,Metadata 主要用於描述 Klass 相關信息
Klass 系統結構以下
// The klass hierarchy is separate from the oop hierarchy.
class Klass;
class InstanceKlass;
class InstanceMirrorKlass;
class InstanceClassLoaderKlass;
class InstanceRefKlass;
class ArrayKlass;
class ObjArrayKlass;
class TypeArrayKlass;
複製代碼
class 向 JVM 提供了 2 個功能
而 Klass 又是繼承於 Metadata,所以像 Method、ConstantPool 都會以成員變量(或指針)的形式存在於 klass 體系中。
由此咱們來總結一下,當咱們 new 一個對象的時候,JVM 首先會判斷這個類是否加載沒有的話會進行加載並建立一個 instanceKlass 對象來描述 Java 類,用它能夠獲取到類對應的元數據信息如運行時常量池等信息,而後到初始化的時候會建立一個 instanceOopDesc 來表示這個 Java 對象實例,而後會進行 oop 的填充,包括填充對象頭,使元數據指針指向 instanceKlass 類,而後填充實例數據。 建立完成後,instanceKlass 保存在方法區中,instanceOopDesc 保存在堆中,對象的引用則保存在棧中。
talk is cheap ,show me the code :
class Model {
public static int a = 1;
public int b;
public Model(int b) {
this.b = b;
}
}
public static void main(String[] args) {
int c = 10;
Model modelA = new Model(2);
Model modelB = new Model(3);
}
複製代碼
上面這張圖是借用別人的,在其它文章也出現了屢次,其中 instanceOopDesc(也稱爲對象頭)包含 Mark Word 和元數據指針,我的認爲不許確,instanceOopDesc 在源碼中的定義還包含了實例數據等信息,若是我的理解有錯誤還望指出,感謝。
參考資料