基于: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);
}