oop-klass内存模型

本文详细解析了JVM内部的oop-klass模型,揭示了如何从实例数据、元数据及方法分发规则三个维度描述Java类。通过宏观与微观角度,阐述了oop、klass和handle在JVM中的作用与关联。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

只要是对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模型

那么这里有一个知识点: 什么是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-handle

微观

oop

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

oop类图
这里有个问题,不是说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 提供虚拟机内部的函数分发机制

其类图如下:

klass类图

handle

handle 部分的类图如下:

handle类图

通过对比三者可以发现: 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);
      }
    }
    
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值