对象在堆内存中的结构:
以HotSpot虚拟机为例,对象在堆内存的布局分为三个区域,分别是对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。其中对象头包括有Mark world,元数据指针。
对象头:Mark World用于存储对象运行时的数据,比如HashCode,锁状态标识,GC分代年龄等。元数据指针用于指向方法区中的目标类的类型信息,通过元数据指针可以确定对象的具体类型。
实例数据:用于存储对象中的各种类型的字段信息(包括从父类继承来的)。
对齐填充:对齐填充不一定存在,起到了占位符的作用,没有特别的含义。
HotSpot的对象模型
Hotspot中采用了OOP-Klass模型,它是用来描述Java对象实例的一种模型。
OOP(ordinary object pointer)指的是普通对象指针
Klass用来描述对象实例的具体类型
在HotSpot中用instanceOopDesc和ArrayOopDesc来描述对象头,其中arrayOopDesc对象用于描述数据类型。
Jvm是用C++实现的,所以instanceOopDesc源码是C++.:
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);
}
};
由源码可以看出instanceOopDesc继承自oopDesc:
oopDesc源码:
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
// Fast access to barrier set. Must be initialized.
static BarrierSet* _bs;
...
}
oopDesc中包含两个数据成员:_mark,_metadata。其中_mark对象就是java对象在堆内存中的Mark World。_metadata是一个共用体,其中包含有_klass指针,_compressed_klass。_klass是普通指针,_compressed_klass是压缩类指针,它们就是前面讲到的元数据指针,这两个指针都指向instanceKlass对象,它用来描述对象的具体类型。
instanceKlass的源码如下:
class InstanceKlass: public Klass {
...
enum ClassState {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialized (successfull final state)
initialization_error // error happened during initialization
};
...
}
instanceKlass继承自Klass ,枚举ClassState 用来标识对象的加载进度。
知道了OOP-Klass模型,我们就可以分析Java虚拟机是如何通过栈帧中的对象引用找到对应的对象实例,如下图所示。
从图中可以看出,通过栈帧中的对象引用找到Java堆中的instanceOopDesc对象,再通过instanceOopDesc中的元数据指针来找到方法区中的instanceKlass,从而确定该对象的具体类型。