oop-klass内存模型

只要是对JVM有所了解的,或多或少的都知道oop-klass模型.那么什么是oop-klass模型?

JVM内部基于oop-klass模型描述一个java类,将一个java类分为两个部分进行描述,其中第一个模型是oop,第二个模型是klass.

接下来,本文从以下几个角度进行讲解:

  1. 宏观: 从整体上描述一下oop-klass模型
  2. 微观: 从类的继承结构上进行讲述,加深理解

宏观

JVM使用oop-klass这种模型来描述一个java类.虽然模型有2个,但是确实从3个不同的角度去看待java类的:

  1. 实例数据角度: jvm使用oop来保存用户的实例数据
  2. 元数据角度: jvm将类的元数据保存到了klass模型中
  3. 方法分发规则: 为了实现多态,jvm 采用了 vtable,itable技术,而vtable,itable 就是保存在klass模型中.

如图所示:
oop-klass内存模型_第1张图片

那么这里有一个知识点: 什么是vtable,itable呢?

简要概括就是:

vtable: 该类所有的函数(除了static, final)和 父类的函数虚拟表。

Itable: 该类所有实现接口的函数列表.

oop-klass体系概览

JVM中使用了oop,klass,handle 这三个类来描述oop-klass.需要注意一点的是: 这三个类不仅能够描述外在的java类,也能描述JVM内在的类型(如 constantPoolOop).

oop,klass之前有介绍了,那么handle是啥意思?

handle 是对oop的行为的封装.这里需要注意的是:

  1. 大多数情况下,JVM在访问java类时是一定通过handle的_handle 来得到oop,再通过oop获得对应的klass,这样,handle就能够访问oop的函数了.
  2. 如果是调用JVM内部的c++类所对应的oop函数,则不需要通过handle,直接通过oop拿到指定的klass即可.

其中的关系如下:

oop-klass内存模型_第2张图片

微观

oop

oop 就是 ordinary object pointer,也即普通对象指针.oop成员众多,其类图如下:

oop-klass内存模型_第3张图片
这里有个问题,不是说xxxoop吗?怎么类图上怎么就是xxxppDesc了?
原因在于: oopsHierarchy.hpp 中进行了如下定义:

typedef class oopDesc*                            oop; 
typedef class   instanceOopDesc*            instanceOop; 
typedef class   methodOopDesc*                    methodOop; 
typedef class   constMethodOopDesc*            constMethodOop; 
typedef class   methodDataOopDesc*            methodDataOop; 
typedef class   arrayOopDesc*                    arrayOop; 
typedef class     objArrayOopDesc*            objArrayOop; 
typedef class     typeArrayOopDesc*            typeArrayOop; 
typedef class   constantPoolOopDesc*            constantPoolOop; 
typedef class   constantPoolCacheOopDesc*   constantPoolCacheOop; 
typedef class   symbolOopDesc*                    symbolOop;
typedef class   klassOopDesc*                    klassOop; 
typedef class   markOopDesc*                    markOop; 
typedef class   compiledICHolderOopDesc*    compiledICHolderOop;

其中各类的说明如下:

类名 说明
oop oop的顶级父类
instanceOop 表示java类实例
methodOop 表示java方法
constMethodOop 表示java方法中的只读信息(其实就是字节码指令)
methodDataOop 表示性能统计的相关数据
arrayOop 数组对象,定义了数组oops的抽象基类
objArrayOop 表示引用类型数组对象
typeArrayOop 表示基本类型数组对象
constantPoolOop 表示java字节码文件中的常量池
constantPoolCacheOop 与constantPoolOop 相伴生,是后者的缓存对象,缓存了字段和方法的访问信息,为允许时环境快速访问字段和方法提供重要作用
symbolOop symbolOop是规范化字符串。所有symbolOop都位于全局符号表中。
klassOop 指向jvm内部的klass实例的对象
markOop oop 的标记对象,表示对象头
compiledICHolderOop 内联缓存实现的帮助器对象。它包含从编译到解释调用转换时使用的中间值(method+klass对),总是分配在永久区(以避免在清除期间遍历codecache)

klass

klass: 按照官方解释,klass 主要提供下面2种能力:

  1. klass 提供一个与java类对等的c++类型描述
  2. klass 提供虚拟机内部的函数分发机制

其类图如下:

oop-klass内存模型_第4张图片

handle

handle 部分的类图如下:

oop-klass内存模型_第5张图片

通过对比三者可以发现: handle 分别给oop和klass定义了不同的体系?这与我们之前给handle的定义有出入–> handle 是对oop的行为的封装.

那么这部分的原因很简单: 在jvm中,使用oop-klass这种一分为二的模型去描述java类以及JVM内部的特殊类,为此JVM内部定义了各种oop和klass类型.但是,对于每一个oop,其实都是一个C++类型,也即klass;而对于每一个klass所对应的class,在JVM内部又会被封装成oop.

JVM在加载类时,会使用oop去存储这个类型的实例数据,并使用klass去存储这个类型的元数据和虚方法表.而当一个类型完成其生命周期后,JVM会通过GC去回收,在回收时,既要回收一个类实例所对应的实例数据oop,也要回收其所对应的元数据和方法分发表(当然,二者的回收时机是不同的).为了让GC即能回收oop也能回收klass,因此oop本身被封装成oop,klass也被封装成oop.

三者关系的实现

  1. handle与oop关联:

    在handle继承体系中, handle这个顶层类中,定义了如下字段:

     oop* _handle;
    

    而该字段会在实例化的时候,会通过构造器进行赋值:

    Handle::Handle(Thread* thread, oop obj) {
      assert(thread == Thread::current(), "sanity check");
      if (obj == NULL) {
        _handle = NULL;
         } else {
        _handle = thread->handle_area()->allocate_handle(obj);
      }
     }
     
    inline Handle::Handle(oop obj) {
      if (obj == NULL) {
        _handle = NULL;
      } else {
        _handle = Thread::current()->handle_area()->allocate_handle(obj);
      }
    }
    
  2. oop与klass进行关联:

    oopDesc做为oop体系的顶层类,其中定义了如下字段:

     union _metadata {
     wideKlassOop    _klass;
     narrowOop       _compressed_klass;
     } _metadata;
    

    而wideKlassOop类型为:klassOopDesc*
    narrowOop类型为:uint32_t(jdk 1.6)

    当开启了指针压缩时,会使用narrowOop,反之,使用wideKlassOop 来指向klassOopDesc.

    该字段会通过oopDesc::set_klass(klassOop k) 来进行设置,如下:

    inline void oopDesc::set_klass(klassOop k) {
      // since klasses are promoted no store check is needed
      assert(Universe::is_bootstrapping() || k != NULL, "must be a real klassOop");
      assert(Universe::is_bootstrapping() || k->is_klass(), "not a klassOop");
      if (UseCompressedOops) {
        oop_store_without_check(compressed_klass_addr(), (oop)k);
      } else {
        oop_store_without_check(klass_addr(), (oop) k);
      }
    }
    

你可能感兴趣的:(openjdk,openjdk)