HotSpot Oop/Klass 模型
基於:openjdk-8-src-b132-03_mar_2014
HotSpot JVM 沒有將Java物件直接通過虛擬機器對映到 C++ 物件,而是設計了一個 Oop/Klass 模型,其中 oop 為 Ordinary Object Pointer,用來表示物件的例項資訊;klass 用來表示物件元資料資訊,不是單指 Class 類的物件。
為什麼要設計 Oop/Klass 這種二分模型的實現?
一個原因是不想讓每個物件都包含 vtbl (虛方法表),其中 oop 中不含有任何虛擬函式,虛表只保存於 klass 中,可以進行 method dispatch(方法分派:動態)。
typedef class markOopDesc* markOop;
typedef class oopDesc* oop;
typedef class instanceOopDesc* instanceOop;
markOopDesc(hotspot\src\share\vm\oops\markOop.hpp)
The markOop describes the header of an object.
Note that the mark is not a real oop but just a word.
markOopDesc
描述的是物件頭,它描述的不是一個真正的物件,而是一個字長的資料。在32為機器上,其大小為32位,在64位上為64位。
markOop
markOopDesc
的指標
oopDesc
(hotspot\src\share\vm\oops\oop.hpp)是描述物件例項的基類,它是抽象類。並且不存在虛方法表。
oop
為指向 oopDesc
的指標
class instanceOopDesc : public oopDesc {}
An instanceOop is an instance of a Java Class
Evaluating “new HashTable()” will create an instanceOop.
instanceOopDesc
描述的是一個 JVM 層面的 Java 物件
instanceOop
instanceOopDesc
的指標
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
oopDesc 物件包含兩部分資料:_mark 和 _metadata;
1、_mark 是 markOop 型別物件,用於儲存物件自身的執行時資料,如雜湊碼(HashCode)、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等等。
2、_metadata 是一個聯合體,_klass 為指向 InstanceKlass 物件的指標;_compressed_klass 是一個壓縮指標。在聯合體中,各成員共享一段記憶體空間,一個聯合變數的長度等於各成員中最長的長度。
1與2構成了Java物件的物件頭。
VMStructs:This class is a friend of most classes, to be able to access private fields.
// An InstanceKlass is the VM level representation of a Java class.
// It contains all information needed for at class at execution runtime.
// InstanceKlass layout:
// [C++ vtbl pointer ] Klass
// [subtype cache ] Klass
// [instance size ] Klass
// [java mirror ] Klass
// [super ] Klass
// [access_flags ] Klass
// [name ] Klass
// [first subklass ] Klass
// [next sibling ] Klass
// [array klasses ]
// [methods ]
// [local interfaces ]
// [transitive interfaces ]
// [fields ]
// [constants ]
// [class loader ]
// [source file name ]
// [inner classes ]
// [static field size ]
// [nonstatic field size ]
// [static oop fields size ]
// [nonstatic oop maps size ]
// [has finalize method ]
// [deoptimization mark bit ]
// [initialization state ]
// [initializing thread ]
// [Java vtable length ]
// [oop map cache (stack maps) ]
// [EMBEDDED Java vtable ] size in words = vtable_len
// [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size
// The embedded nonstatic oop-map blocks are short pairs (offset, length)
// indicating where oops are located in instances of this klass.
// [EMBEDDED implementor of the interface] only exist for interface
// [EMBEDDED host klass ] only exist for an anonymous class (JSR 292 enabled)
HotSpot VM 裡,Klass 用於描述能被 GC 的物件的型別資訊的元資料物件。而 Oop 則用來描述能被 GC 的 Java 物件。
當在 Java 中 new 一個物件時,本質是在堆記憶體建立一個 instanceOopDesc物件。instanceOopDesc 物件通過元資料指標(_klass或者_compressed_klass)指向方法區的元資料型別的資訊(InstanceKlass )。
JVM可以通過物件引用準確定位到 Java 堆區中的 instanceOopDesc 物件,這樣既可成功訪問到物件的例項資訊,當需要訪問目標物件的具體型別時,JVM則會通過儲存在 instanceOopDesc 中的元資料指標定位到儲存在方法區中的 instanceKlass 物件上。
方法區是 JVM 的規範,不同虛擬機器有不同的實現方式,我們常說的HotSpot虛擬機器,它1.7版本實現方式就是永久代(PermGen),1.8版本是元空間(Metaspace)。
PermGen 是在 JVM 記憶體區內(not Heap),受到 JVM 記憶體限制制約。Metaspace 屬於原生記憶體在 JVM 之外,基本上機器有多少記憶體就可以多大。
JVM 中,InstanceKlass、java.lang.Class的關係?
An InstanceKlass is the VM level representation of a Java class.
It contains all information needed for at class at execution runtime.
ClassFileParser 將 class 檔案在 runtime 解析成一個個 InstanceKlass 物件,這個物件是靜態位元組碼檔案在執行時 Metaspace 空間的一個對映。我們知道Java是一種支援反射的語言,為了能在 Java 層實現對定義型別的解構,JVM實現了 InstanceKlass 的一個 java mirror 的概念——java.lang.Class 物件。
InstanceKlass類繼承自Klass類,在Klass類中有一個成員變數,並且提供了相應的Setter/Getter函式實現:
// java/lang/Class instance mirroring this class
oop _java_mirror;
oop java_mirror() const { return _java_mirror; }
void set_java_mirror(oop m) { klass_oop_store(&_java_mirror, m); }
在 java_lang_Class 類中,也提供了 Class 物件與 Klass 物件的轉化函式:
static Klass* as_Klass(oop java_class);
static void set_klass(oop java_class, Klass* klass);
Class類所提供的反射機制,最終都是通過JNI介面,呼叫相應的native方法,然後通過 as_Klass 函式轉換成 InstanceKlass 物件,拿到定義型別的元資料資訊的。
實戰下:
// -XX:+UseSerialGC -XX:-UseCompressedOops -Xms10m -Xmx10m
class Father {
}
class Son extends Father {
private static final String COUNTRY = "China";
}
public class Main {
public static void main(String[] args) {
Son son = new Son();
}
}
Microsoft Windows [版本 10.0.14393]
(c) 2016 Microsoft Corporation。保留所有權利。
E:\t00ls\Merry>jps
1504 RemoteMavenServer
10372
5108 Main
1016 Jps
5372 HSDB
8972 Launcher
E:\t00ls\Merry>java -cp .;%JAVA_HOME%/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
hsdb> attach 5108
Attaching to process 5108, please wait...
hsdb> universe ::列印堆資訊
Heap Parameters:
:: 新生代
Gen 0: eden [0x0000000012c00000,0x0000000012c1ea18,0x0000000012eb0000) space capacity = 2818048, 4.452159792877907 used
from [0x0000000012f00000,0x0000000012f4fff8,0x0000000012f50000) space capacity = 327680, 99.99755859375 used
to [0x0000000012eb0000,0x0000000012eb0000,0x0000000012f00000) space capacity = 327680, 0.0 usedInvocations: 1
::老年代
Gen 1: old [0x0000000012f50000,0x0000000012ff5ee0,0x0000000013600000) space capacity = 7012352, 9.69215464369159 usedInvocations: 0
:: 掃描 新生代 Eden 裡面的 Son 物件
hsdb> scanoops 0x0000000012c00000 0x0000000012eb0000 test.Son
0x0000000012c14840 test/Son
Error: sun.jvm.hotspot.types.WrongTypeException: No suitable match for type of address 0x0000000012c14868
hsdb> whatis 0x0000000012c14840
Address 0x0000000012c14840: In thread-local allocation buffer for thread "main" (4) [0x0000000012c111b8,0x0000000012c14850,0x0000000012c1ea00,{0x0000000012c1ea18})
:: 執行緒 TLAB 內分配的空間
hsdb> inspect 0x0000000012c14840
instance of Oop for test/Son @ 0x0000000012c14840 @ 0x0000000012c14840 (size = 16) :: 物件大小
_mark: 1
_metadata._klass: InstanceKlass for test/Son
hsdb> mem 0x0000000012c14840 2
0x0000000012c14840: 0x0000000000000001
0x0000000012c14848: 0x0000000013a12410 ::型別指標
hsdb> whatis 0x0000000013a12410
pointer to InstanceKlass
hsdb> inspect 0x0000000013a12410
Type is InstanceKlass (size of 440)
juint Klass::_super_check_offset: 56
Klass* Klass::_secondary_super_cache: Klass @ null
Array<Klass*>* Klass::_secondary_supers: Array<Klass*> @ 0x0000000013600f88
Klass* Klass::_primary_supers[0]: Klass @ 0x0000000013601c00
oop Klass::_java_mirror: Oop for java/lang/Class @ 0x0000000012c14718 Oop for java/lang/Class @ 0x0000000012c14718
jint Klass::_modifier_flags: 0
Klass* Klass::_super: Klass @ 0x0000000013a12208
Klass* Klass::_subklass: Klass @ null
jint Klass::_layout_helper: 16
Symbol* Klass::_name: Symbol @ 0x0000000014912ba0
AccessFlags Klass::_access_flags: 538968096
markOop Klass::_prototype_header: 5
Klass* Klass::_next_sibling: Klass @ null
u8 Klass::_trace_id: 38141952
Klass* InstanceKlass::_array_klasses: Klass @ null
Array<Method*>* InstanceKlass::_methods: Array<Method*> @ 0x0000000013a11f98
Array<Method*>* InstanceKlass::_default_methods: Array<Method*> @ null
Array<Klass*>* InstanceKlass::_local_interfaces: Array<Klass*> @ 0x0000000013600f88
Array<Klass*>* InstanceKlass::_transitive_interfaces: Array<Klass*> @ 0x0000000013600f88
Array<u2>* InstanceKlass::_fields: Array<u2> @ 0x0000000013a11f80
u2 InstanceKlass::_java_fields_count: 1
ConstantPool* InstanceKlass::_constants: ConstantPool @ 0x0000000013a11e88
ClassLoaderData* InstanceKlass::_class_loader_data: ClassLoaderData @ 0x0000000013edfab0
u2 InstanceKlass::_source_file_name_index: 16
char* InstanceKlass::_source_debug_extension: char @ null
Array<jushort>* InstanceKlass::_inner_classes: Array<jushort> @ 0x0000000013600f58
int InstanceKlass::_nonstatic_field_size: 0
int InstanceKlass::_static_field_size: 1
u2 InstanceKlass::_static_oop_field_count: 1
int InstanceKlass::_nonstatic_oop_map_size: 0
bool InstanceKlass::_is_marked_dependent: 0
u2 InstanceKlass::_minor_version: 0
u2 InstanceKlass::_major_version: 52
u1 InstanceKlass::_init_state: 4
Thread* InstanceKlass::_init_thread: Thread @ 0x000000000305e800
int InstanceKlass::_vtable_len: 5
int InstanceKlass::_itable_len: 2
u1 InstanceKlass::_reference_type: 0
OopMapCache* InstanceKlass::_oop_map_cache: OopMapCache @ null
JNIid* InstanceKlass::_jni_ids: JNIid @ 0x0000000014a15520
nmethod* InstanceKlass::_osr_nmethods_head: nmethod @ null
BreakpointInfo* InstanceKlass::_breakpoints: BreakpointInfo @ null
u2 InstanceKlass::_generic_signature_index: 0
jmethodID* InstanceKlass::_methods_jmethod_ids: jmethodID @ 0x0000000014a15480
u2 InstanceKlass::_idnum_allocated_count: 1
Annotations* InstanceKlass::_annotations: Annotations @ null
nmethodBucket* InstanceKlass::_dependencies: nmethodBucket @ null
Array<int>* InstanceKlass::_method_ordering: Array<int> @ 0x0000000013a12600
Array<int>* InstanceKlass::_default_vtable_indices: Array<int> @ null
hsdb>
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);
}