浅谈JVM OOP-Klass二分模型

当创建一个对象的时候,你有没有发现新生区和元数据区内存占用都有所增加呢?而这和OOP-Klass二分模型有关。

OOP-Klass二分模型介绍

HotSpot中采用了OOP-Klass模型,它是用来描述Java对象实例的一种模型

  • OOP或OOPS(Ordinary Object Pointer)指的是普通对象指针,主要职能是表示对象的实例数据,存储在堆里面
  • Klass用来描述对象实例的具体类型,实现语言层面的Java类,存储在元空间(方法区)

OOP

在Java应用程序运行过程中,每创建一个Java对象,在JVM内部也会相应创建一个OOP对象来表示Java对象。OOP类的共同基类型是oopDesc

根据JVM内部使用的对象业务类型,具有多种oopDesc子类,比如instanceOopDesc表示类实例,arrayOopDesc表示数组。

其中,instanceOopDesc和arrayOopDesc又称为对象头,instanceOopDesc对象头包括以下两部分信息:Mark Word 和 元数据指针(Klass*):

  1. Mark Word,主要存储对象运行时记录信息,如hashcode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;
  2. 元数据指针,instanceOopDesc中的_metadata成员,它是联合体,可以表示未压缩的Klass指针(_klass)和压缩的Klass指针。对应的klass指针指向一个存储类的元数据的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与instanceKlass

Klass数据结构定义类所有Klass类型共享的结构和行为,描述类型自身的布局,以及与其他类之间的关系(父类、子类、兄弟类等)
浅谈JVM OOP-Klass二分模型_第1张图片

  • _layout_helper:描述对象整体布局
  • _name:表示类名
  • _java_mirror:表示Klass的Java层镜像类
  • _super:表示父类
  • _subklass:表示第一个子类
  • next_slibling:指向的是下一个兄弟节点,JVM通过_subklass->next_slibling可以找到下一个子类

在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虚拟机是如何通过栈帧中的对象引用找到对应的对象实例。
浅谈JVM OOP-Klass二分模型_第2张图片
从图中可以看出,通过栈帧中的对象引用找到Java堆中的instanceOop对象,当需要调用对象方法或访问类变量,可以再通过instanceOop中持有的类元数据指针来找到方法区中的instanceKlass对象来完成。

klass和oop之间的联系

浅谈JVM OOP-Klass二分模型_第3张图片

总结

当我们执行new Object()的时候,首先JVM native层判断该类是否被加载过,没有的话就进行类的加载,并在JVM内部创建一个instanceKlass对象表示该类的运行时元数据(Java层的Class对象),到初始化的时候,JVM就创建一个instanceOopDesc对象表示该对象的实例,然后进行Mark Word信息填充,将元数据指针指向Klass对象,并填充实例变量

你可能感兴趣的:(Java虚拟机)