当创建一个对象的时候,你有没有发现新生区和元数据区内存占用都有所增加呢?而这和OOP-Klass二分模型有关。
HotSpot中采用了OOP-Klass模型,它是用来描述Java对象实例的一种模型
在Java应用程序运行过程中,每创建一个Java对象,在JVM内部也会相应创建一个OOP对象来表示Java对象。OOP类的共同基类型是oopDesc
根据JVM内部使用的对象业务类型,具有多种oopDesc子类,比如instanceOopDesc表示类实例,arrayOopDesc表示数组。
其中,instanceOopDesc和arrayOopDesc又称为对象头,instanceOopDesc对象头包括以下两部分信息:Mark Word 和 元数据指针(Klass*):
oopDesc.hpp
//hotspot/src/share/vm/oops/oop.hpp
class oopDesc {
//....
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
//....
}
oopDesc中包含两个数据成员:_mark 和 _metadata。其中markOop类型的_mark对象指的是前面讲到的Mark World。_metadata是一个共用体,其中_klass是普通指针,_compressed_klass是压缩类指针,它们就是前面讲到的元数据指针,这两个指针都指向instanceKlass对象,它用来描述对象的具体类型。
Klass数据结构定义类所有Klass类型共享的结构和行为,描述类型自身的布局,以及与其他类之间的关系(父类、子类、兄弟类等)
在HotSpot中,为每一个已加载的Java类创建一个instanceKlass对象,用来标识Java内部类型的机制。instanceKlass对象的所有成员可以包含JVM内部运行一个Java类所需的所有信息,这些成员变量在类解析阶段完成赋值。
instanceKlass.hpp
class instanceKlass: public Klass {
friend class VMStructs;
public:
enum ClassState {
unparsable_by_gc = 0, // object is not yet parsable by gc. Value of _init_state at object allocation.
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
};
//部分内容省略
protected:
// Method array. 方法数组
objArrayOop _methods;
// Interface (klassOops) this class declares locally to implement.
objArrayOop _local_interfaces; //该类声明要实现的接口.
// Instance and static variable information
typeArrayOop _fields;
// Constant pool for this class.
constantPoolOop _constants; //常量池
// Class loader used to load this class, NULL if VM loader used.
oop _class_loader; //类加载器
typeArrayOop _inner_classes; //内部类
Symbol* _source_file_name; //源文件名
}
其中,ClassState描述了类加载的状态:分配、加载、链接、初始化。
instanceKlass的布局包括:声明接口、字段、方法、常量池、源文件名等等
通过OOP-Klass模型,就可以分析出Java虚拟机是如何通过栈帧中的对象引用找到对应的对象实例。
从图中可以看出,通过栈帧中的对象引用找到Java堆中的instanceOop对象,当需要调用对象方法或访问类变量,可以再通过instanceOop中持有的类元数据指针来找到方法区中的instanceKlass对象来完成。
当我们执行new Object()的时候,首先JVM native层判断该类是否被加载过,没有的话就进行类的加载,并在JVM内部创建一个instanceKlass对象表示该类的运行时元数据(Java层的Class对象),到初始化的时候,JVM就创建一个instanceOopDesc对象表示该对象的实例,然后进行Mark Word信息填充,将元数据指针指向Klass对象,并填充实例变量。