1. 程式人生 > >HotSpot Oop/Klass 模型

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);
}

這裡寫圖片描述