您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
JVM中的oop-klass内存模型
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
JVM中的oop-klass内存模型
京东科技IoT团队
2021-01-06
IP归属:未知
78720浏览
只要是对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模型](//img1.jcloudcs.com/developer.jdcloud.com/61698983-deeb-42b4-8152-e64b9b5688fc20210106000903.jpeg) 那么这里有一个知识点: 什么是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](//img1.jcloudcs.com/developer.jdcloud.com/c0d86bee-3d08-4fe6-8ff0-822bf5e9c77620210106000929.jpeg) # 微观 ## oop oop 就是 ordinary object pointer,也即普通对象指针.oop成员众多,其类图如下: ![oop类图](//img1.jcloudcs.com/developer.jdcloud.com/9972b088-f835-4e62-9123-157fee4414d320210106001643.jpeg) 这里有个问题,不是说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; ``` 其中各类的说明如下: <table><tr><td>类名</td><td>说明</td></tr><tr><td> oop </td><td>oop的顶级父类</td></tr><tr><td> instanceOop </td><td>表示java类实例</td></tr><tr><td> methodOop </td><td>表示java方法</td></tr><tr><td> constMethodOop </td><td>表示java方法中的只读信息(其实就是字节码指令)</td></tr><tr><td> methodDataOop </td><td>表示性能统计的相关数据</td></tr><tr><td> arrayOop </td><td>数组对象,定义了数组oops的抽象基类</td></tr><tr><td> objArrayOop </td><td>表示引用类型数组对象</td></tr><tr><td> typeArrayOop </td><td>表示基本类型数组对象</td></tr><tr><td> constantPoolOop </td><td>表示java字节码文件中的常量池</td></tr><tr><td> constantPoolCacheOop </td><td>与constantPoolOop 相伴生,是后者的缓存对象,缓存了字段和方法的访问信息,为允许时环境快速访问字段和方法提供重要作用</td></tr> <tr><td> symbolOop </td><td> symbolOop是规范化字符串。所有symbolOop都位于全局符号表中。</td></tr><tr><td> klassOop </td><td>指向jvm内部的klass实例的对象</td></tr> <tr><td> markOop </td><td>oop 的标记对象,表示对象头</td></tr> <tr><td> compiledICHolderOop </td><td>内联缓存实现的帮助器对象。它包含从编译到解释调用转换时使用的中间值(method+klass对),总是分配在永久区(以避免在清除期间遍历codecache)</td></tr></table> ## klass klass: 按照官方解释,klass 主要提供下面2种能力: 1. klass 提供一个与java类对等的c++类型描述 2. klass 提供虚拟机内部的函数分发机制 其类图如下: ![klass类图](//img1.jcloudcs.com/developer.jdcloud.com/96ae69ef-fce7-4ba2-81ba-3771dfa4c38520210106001254.jpeg) ## handle handle 部分的类图如下: ![handle类图](//img1.jcloudcs.com/developer.jdcloud.com/5eb1e153-052f-4680-96ac-73b4839c7a8620210106001220.jpeg) 通过对比三者可以发现: 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); } } ``` > 作者:何佳瑞
原创文章,需联系作者,授权转载
上一篇:Insight Mybatis 日志打印及使用建议
下一篇:智能协同开放平台金融行业解决方案
京东科技IoT团队
文章数
13
阅读量
110382
作者其他文章
01
前端 | 小程序横竖屏的坑和 rpx 布局方案
如何避免小程序开发过程中的那些“坑”
01
前端 | Chrome 80 中 Iframe cookie 无法携带的问题
Chrome 80 中 Iframe cookie 无法携带的问题求解过程。
01
NLU | 智能音箱语义理解——MDIS三合一模型
MDIS模型(Unified Model for Multiple Domain Intent and Slot)可以做到同时对话术进行领域分类、意图判断以及填槽。
01
前端 |数据大屏适配方案
数据大屏适配方案详解
最新回复
丨
点赞排行
共0条评论
京东科技IoT团队
文章数
13
阅读量
110382
作者其他文章
01
前端 | 小程序横竖屏的坑和 rpx 布局方案
01
前端 | Chrome 80 中 Iframe cookie 无法携带的问题
01
NLU | 智能音箱语义理解——MDIS三合一模型
01
前端 |数据大屏适配方案
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号