HotSpot中采用了OOP-Klass模型,它是用来描述Java对象实例的一种模型
OOP或OOPS(Ordinary Object Pointer)指的是普通对象指针,主要职能是表示对象的实例数据,存储在堆里面Klass用来描述对象实例的具体类型,实现语言层面的Java类,存储在元空间(方法区)总体上是多个OOP和一个Klass是对应的. 相当于一个类可以有多个实例
A a = new A() ; A a1 = new A();
a,a1 分别对应着不同的OOP, 对应着同一个Klass
首先看 oopsHierarchy.hpp 文件中,可以看到在 OpenJDK 的源码中都是用 oopDesc* 等 Desc* 来表示的。
列出的是整个Oops模块的组成结构,其中包含多个子模块。每一个子模块对应一个类型,每一个类型的OOP都代表一个在JVM内部使用的特定对象的类型。
//定义了oops共同基类 typedef class oopDesc* oop; //表示一个Java类型实例 typedef class instanceOopDesc* instanceOop; //表示一个Java方法 typedef class methodOopDesc* methodOop; //表示一个Java方法中的不变信息 typedef class constMethodOopDesc* constMethodOop; //记录性能信息的数据结构 typedef class methodDataOopDesc* methodDataOop; //定义了数组OOPS的抽象基类 typedef class arrayOopDesc* arrayOop; //表示持有一个OOPS数组 typedef class objArrayOopDesc* objArrayOop; //表示容纳基本类型的数组 typedef class typeArrayOopDesc* typeArrayOop; //表示在Class文件中描述的常量池 typedef class constantPoolOopDesc* constantPoolOop; //常量池告诉缓存 typedef class constantPoolCacheOopDesc* constantPoolCacheOop; //描述一个与Java类对等的C++类 typedef class klassOopDesc* klassOop; //表示对象头 typedef class markOopDesc* markOop;
在Java程序运行过程中,每创建一个新的对象,在JVM内部就会相应地创建一个对应类型的OOP对象。在HotSpot中,根据JVM内部使用的对象业务类型,具有多种oopDesc的子类。除了oppDesc类型外,opp体系中还有很多instanceOopDesc、arrayOopDesc 等类型的实例,他们都是oopDesc的子类。
这些OOPS在JVM内部有着不同的用途,例如,instanceOopDesc表示类实例,arrayOopDesc表示数组。也就是说,当我们使用new创建一个Java对象实例的时候,JVM会创建一个instanceOopDesc对象来表示这个Java对象。同理,当我们使用new创建一个Java数组实例的时候,JVM会创建一个arrayOopDesc对象来表示这个数组对象。
通过上面的源码可以看到,instanceOopDesc实际上就是继承了oopDesc,并没有增加其他的数据结构,也就是说instanceOopDesc中包含两部分数据:markOop _mark和union _metadata。
这里就和java对象头对起来了。
friend class VMStructs; friend class JVMCIVMStructs; private: // 看 MarkOop 的内容 volatile markOop _mark; // 存储对象描述数据的指针 union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metadata;和oopDesc是其他oop类型的父类一样,Klass类是其他klass类型的父类。
Klass向JVM提供两个功能:
实现语言层面的Java类(在Klass基类中已经实现)
实现Java对象的分发功能(由Klass的子类提供虚函数实现)
HotSopt JVM的设计者把对象一拆为二,分为klass和oop,其中oop的职能主要在于表示对象的实例数据(是一个指针,作为一个java对象头存在),所以其中不含有任何虚函数。而klass为了实现虚函数多态,所以提供了虚函数表。所以,关于Java的多态,其实也有虚函数的影子在。
_metadata是一个共用体,其中_klass是普通指针,_compressed_klass是压缩类指针。这两个指针都指向instanceKlass对象,它用来描述对象的具体类型。
JVM在运行时,需要一种用来标识Java内部类型的机制。在HotSpot中的解决方案是:为每一个已加载的Java类创建一个instanceKlass对象,用来在JVM层表示Java类。
来看下instanceKlass的内部结构:(将class文件相应数据读取到元空间所表现的形式)
//类拥有的方法列表 objArrayOop _methods; //描述方法顺序 typeArrayOop _method_ordering; //实现的接口 objArrayOop _local_interfaces; //继承的接口 objArrayOop _transitive_interfaces; //域 typeArrayOop _fields; //常量 constantPoolOop _constants; //类加载器 oop _class_loader; //protected域 oop _protection_domain; ....注意:
在JVM中,对象在内存中的基本存在形式就是oop。那么,对象所属的类,在JVM中也是一种对象,因此它们实际上也会被组织成一种oop,即klassOop。同样的,对于klassOop,也有对应的一个klass来描述,它就是klassKlass,也是klass的一个子类。klassKlass作为oop的klass链的端点。
关于对象和数组的klass链大致如下图:
对象的实例(instantOopDesc)保存在堆上,对象的元数据(instantKlass)保存在方法区,对象的引用保存在栈上。
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); }
此处做了简单理解,也就是把常量池相关数据直接用方法区进行了展示,整正的操作应该是有三个。既对象的OOP,对象对应的java.lang.Class的OOP,这两个在堆上,还有在元空间的instanceklass。