java的对象模型

news2024/10/7 6:43:35

背过面试题的朋友都知道,Java对象是保存在堆内存中,在内存中,一个Java对象包含三部分:对象头,实例数据,对其填充。其中对象头是一个很关键的部分,因为对象头中包含锁状态标志,线程持有锁的等标志,

OOP-Klass model

HotSpot是基于c++实现,而c++是一门面向对象的语言,本身是具备面向对象基本特征的,所以Java中的对象表示,最简单的做法是为每个Java类生成一个c++类与之对应。但HotSpot JVM并没有这么做,而是设计了一个OOP-Klass Model。OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象实例的具体类型。

为什么HotSpot要设计一套oop-klass model呢?答案是:HotSopt JVM的设计者不想让每个对象中都含有一个vtable(虚函数表)

这个解释似乎可以说得通。众所周知,C++和Java都是面向对象的语言,面向对象语言有一个很重要的特性就是多态。关于多态的实现,C++和Java有着本质的区别。

多态是面向对象的最主要的特性之一,是一种方法的动态绑定,实现运行时的类型决定对象的行为。多态的表现形式是父类指针或引用指向子类对象,在这个指针上调用的方法使用子类的实现版本。多态是IOC、模板模式实现的关键。

在C++中通过虚函数表的方式实现多态,每个包含虚函数的类都具有一个虚函数表(virtual table),在这个类对象的地址空间的最靠前的位置存有指向虚函数表的指针。在虚函数表中,按照声明顺序依次排列所有的虚函数。由于C++在运行时并不维护类型信息,所以在编译时直接在子类的虚函数表中将被子类重写的方法替换掉。

在Java中,在运行时会维持类型信息以及类的继承体系。每一个类会在方法区中对应一个数据结构用于存放类的信息,可以通过Class对象访问这个数据结构。其中,类型信息具有superclass属性指示了其超类,以及这个类对应的方法表(其中只包含这个类定义的方法,不包括从超类继承来的)。而每一个在堆上创建的对象,都具有一个指向方法区类型信息数据结构的指针,通过这个指针可以确定对象的类型。

上面这段是我从网上摘取过来的,说的有一定道理,但是也不全对。至于为啥,我会在后文介绍到Klass的时候细说。

关于opp-klass模型的整体定义,在HotSpot的源码中可以找到。

oops模块可以分成两个相对独立的部分:OOP框架和Klass框架。

在oopsHierarchy.hpp里定义了oop和klass各自的体系。

oop体系

//定义了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;

 

上面列出的是整个Oops模块的组成结构,其中包含多个子模块。每一个子模块对应一个类型,每一个类型的OOP都代表一个在JVM内部使用的特定对象的类型。

从上面的代码中可以看到,有一个变量opp的类型是oppDesc ,OOPS类的共同基类型为oopDesc

class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;
  union _metadata {
    wideKlassOop    _klass;
    narrowOop       _compressed_klass;
  } _metadata;
}

 在Java程序运行过程中,每创建一个新的对象,在JVM内部就会相应地创建一个对应类型的OOP对象。在HotSpot中,根据JVM内部使用的对象业务类型,具有多种oopDesc的子类。除了oppDesc类型外,opp体系中还有很多instanceOopDescarrayOopDesc 等类型的实例,他们都是oopDesc的子类。

这些OOPS在JVM内部有着不同的用途,例如instanceOopDesc表示类实例,arrayOopDesc表示数组。也就是说,当我们使用new创建一个Java对象实例的时候,JVM会创建一个instanceOopDesc对象来表示这个Java对象。同理,当我们使用new创建一个Java数组实例的时候,JVM会创建一个arrayOopDesc对象来表示这个数组对象。

通过上面的源码可以看到,instanceOopDesc实际上就是继承了oopDesc,并没有增加其他的数据结构,也就是说instanceOopDesc中包含两部分数据:markOop _markunion _metadata

这里的markOop你可能又熟悉了,这不就是OOPS体系中的一部分吗,上面注释中已经说过,他表示对象头。 _metadata是一个联合体,这个字段被称为元数据指针。指向描述类型Klass对象的指针。

HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头、实例数据和对齐填充。在虚拟机内部,一个Java对象对应一个instanceOopDesc的对象,该对象中有两个字段分别表示了对象头和实例数据。那就是_mark_metadata

前面介绍到的_metadata是一个共用体,其中_klass是普通指针,_compressed_klass是压缩类指针。在深入介绍之前,就要来到oop-Klass中的另外一个主角klass了。

klass

klass体系 

//klassOop的一部分,用来描述语言层的类型
class  Klass;
//在虚拟机层面描述一个Java类
class   instanceKlass;
//专有instantKlass,表示java.lang.Class的Klass
class     instanceMirrorKlass;
//专有instantKlass,表示java.lang.ref.Reference的子类的Klass
class     instanceRefKlass;
//表示methodOop的Klass
class   methodKlass;
//表示constMethodOop的Klass
class   constMethodKlass;
//表示methodDataOop的Klass
class   methodDataKlass;
//最为klass链的端点,klassKlass的Klass就是它自身
class   klassKlass;
//表示instanceKlass的Klass
class     instanceKlassKlass;
//表示arrayKlass的Klass
class     arrayKlassKlass;
//表示objArrayKlass的Klass
class       objArrayKlassKlass;
//表示typeArrayKlass的Klass
class       typeArrayKlassKlass;
//表示array类型的抽象基类
class   arrayKlass;
//表示objArrayOop的Klass
class     objArrayKlass;
//表示typeArrayOop的Klass
class     typeArrayKlass;
//表示constantPoolOop的Klass
class   constantPoolKlass;
//表示constantPoolCacheOop的Klass
class   constantPoolCacheKlass;

oopDesc是其他oop类型的父类一样,Klass类是其他klass类型的父类。

Klass向JVM提供两个功能:

  • 实现语言层面的Java类(在Klass基类中已经实现)

  • 实现Java对象的分发功能(由Klass的子类提供虚函数实现)

文章开头的时候说过:之所以设计oop-klass模型,是因为HotSopt JVM的设计者不想让每个对象中都含有一个虚函数表。

HotSopt JVM的设计者把对象一拆为二,分为klassoop,其中oop的职能主要在于表示对象的实例数据,所以其中不含有任何虚函数。而klass为了实现虚函数多态,所以提供了虚函数表。所以,关于Java的多态,其实也有虚函数的影子在。

_metadata是一个共用体,其中_klass是普通指针,_compressed_klass是压缩类指针。这两个指针都指向instanceKlass对象,它用来描述对象的具体类型。

instanceKlass

JVM在运行时,需要一种用来标识Java内部类型的机制。在HotSpot中的解决方案是:为每一个已加载的Java类创建一个instanceKlass对象,用来在JVM层表示Java类。

来看下instanceKlass的内部结构:

  //类拥有的方法列表
  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链的端点。

存储内存

关于一个Java对象,他的存储是怎样的,一般很多人会回答:对象存储在堆上。稍微好一点的人会回答:对象存储在堆上,对象的引用存储在栈上。今天,再给你一个更加显得牛逼的回答:

对象的实例(instantOopDesc)保存在堆上,对象的元数据(instantKlass)保存在方法区,对象的引用保存在栈上。

其实如果细追究的话,上面这句话有点故意卖弄的意思。因为我们都知道。方法区用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 所谓加载的类信息,其实不就是给每一个被加载的类都创建了一个 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);
}

 

 

 

总结

每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了两部分信息,方法头以及元数据。对象头中有一些运行时数据,其中就包括和多线程相关的锁的信息。元数据其实维护的是指针,指向的是对象所属的类的instanceKlass

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/502652.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用MindSDK的at-server组件开发从机模组

使用MindSDK的at-server组件开发从机模组 文章目录 使用MindSDK的at-server组件开发从机模组引言AT命令应用场景AT命令技术简介MindSDK中的at-server组件及样例工程at_port.cat_cmd_led.cmain.c 基于AT命令的人机交互应用使用串口调试助手软件发送AT命令使用Python脚本发送AT命…

Java高阶数据结构 并查集 最小生成树

并查集与最小生成树 文章目录 Java高阶数据结构 & 并查集 & 最小生成树1. 并查集1.1 并查集的原理1.1.1 例子:1.1.2 这样存储有什么好处呢? 1.2 并查集的代码实现1.2.1 类的定义与属性1.2.2 构造方法1.2.3 获取下标的方法1.2.4 获得根节点1.2.5 …

1.SpringCloud技术实用02

SpringCloud技术实用02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心,同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。…

ES+Redis+MySQL 高可用架构设计

一、背景 二、ES高可用方案 三、会员Redis缓存方案 四、高可用会员主库方案 五、异常会员关系治理 六、展望:更精细化的流控和降级策略 一、背景 会员系统是一种基础系统,跟公司所有业务线的下单主流程密切相关。如果会员系统出故障,会…

网络安全信息收集初探之域名信息收集

网络安全信息收集初探之域名信息收集 域名信息收集工具oneforall收集子域名扫描单个域名批量扫描域名oneforall 额外参数 google hacking 证书收集子域名证书子域名在线收集网站子域名收集的各种细节 域名信息收集工具 oneforall收集子域名 扫描单个域名 python oneforall.p…

进阶自动化测试,这3点你一定要知道的...

自动化测试指软件测试的自动化,在预设状态下运行应用程序或系统,预设条件包括正常和异常,最后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。 自动化测试框架一般可以分为两个层次,上层是管理整个自动化测试的开发&a…

云渲染农场具有什么特点?

众所周知,渲染农场的出现是为了解决长时间的图像渲染问题。渲染农场的底层搭建原理是利用很多计算机、网络和操作系统来构建一个庞大的计算群组,把一个渲染任务从一台机器分发到这个计算群组,从而达到短时间内能够快速得到渲染结果。 到了20…

分布式搜索引擎es 面试突击

es elastocsearch 倒排索引是在数据查询之前建立,在查询的时候可以直接通过关键词定位到文档内容。用空间换时间 分布式架构原理说一下? es底层是基于lucene来的 大概就是一个用于全文检索的jar包 用es来做分布式的搜索引擎 可以承载一秒钟几千的…

【Vue工程】005-Vue Router

【Vue工程】005-Vue Router 文章目录 【Vue工程】005-Vue Router一、概述1、Slogan2、官网3、参考文章 二、安装三、基本使用1、定义路由2、创建路由实例3、在 main.ts 注册路由4、在 App.vue 定义路由出口 四、嵌套路由1、修改路由2、定义嵌套路由出口 五、配置404页面六、声明…

联合索引该如何选择合适的列?

前面一篇文章,松哥和大家聊了 MySQL 中的索引合并,虽然 MySQL 提供了索引合并机制来提升 SQL 执行的效率,然而在具体实践中,如果能避免发生索引合并是最好的,毕竟这是没办法的办法,是一个下下策。发生索引合…

Wikidata 模型分析+实体抽取+数据处理

Wikidata 数据分析与处理 需求:Wikidata 数据描述了很多实体,以及实体属性。比如某一个公司/组织/机构名称是:阿里巴巴,对数据内该组织的相关属性进行观察、分析、治理、抽取等,最后用图数据库进行存储和展示其关系&am…

为什么半导体FAB生产线需要EAP系统?

在半导体制造中,设备自动化系统EAP(Equipment Automation Program)是不可或缺的重要软件,它是连接MES、RMS、APC、FDC等上层系统和设备层的桥梁,用于管控生产线上的所有机台,并实现设备运行的自动化。 作为…

QT+OpenGL高级数据和高级GLSL

QTOpenGL高级数据和高级GLSL 本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主 高级数据 OpenGL中的缓冲区 对象管理特定的GPU内存 在将缓冲区绑定到特定的缓冲区目标时候赋予它意义 OpenGL在内部会保…

项目环境配置、不知晓问题自己搜索后得到的解答

目录 Anolis OS龙蜥操作系统 Kernel Selection 4.18.0(RHCK) Compatible with RHEL (kernel-4.18.0) 4.19.91(ANCK) Support Anolis OS verified platform (kernel-4.19.91) 这两个内核选择哪个比较好呢? 我的C盘有些满,我该如何删除一些我需要的东西…

docker网络访问和端口映射

docker网络访问和端口映射 文章目录 docker网络访问和端口映射1.docker容器网络1.1.创建一个centos7的容器1.2.docker网络原理图 2.端口映射2.1.创建一个新的IP2.2.多个IP端口映射2.3.随机端口命令 1.docker容器网络 指定映射(docker 会 自动添加一条iptables规则来…

wisp5.0 学习日记2

学习日记 昨天的报错尝试1,在CCS中设置USB FET尝试2 csdn解决方案1尝试3 查看仿真器的驱动是否安装成功 昨天的报错 MSP430: Error initializing emulator: No USB FET was found 尝试1,在CCS中设置USB FET 打开CCS,选择“Window” -> …

在线文档编辑工具哪个更好?

在线文档编辑工具相当于一个轻量级、跨平台、多途径的Office。使用在线文档编辑工具,首先我们不用安装Office软件;其次在电脑网页上、手机小程序里我们都可以使用在线文档进行简单的编辑;最后我们编辑的文档可以实时更新、分享、协作等。今天…

供应商标准化管理难?云时通助力国药器械成功打造医疗器械行业SRM管理平台!

中国医疗器械有限公司(CMDC,简称“国药器械”),始建于1966年,隶属于国药集团,是其医疗器械板块的主力军。国药器械有分子公司300家左右,年销售额300多亿,国内最大的医疗器械商业流通企业,产品覆盖医疗器械所…

软件测试简历?面试题?企业面试官想要什么?我不再和offer失之交臂...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 软件测试面试题简历…

vivado跨时钟域路径分析

若要查看跨时钟域路径分析报告,可选择以下内容之一来查看: A, Reports > Timing > Report Clock Interaction B, Flow Navigator > Synthesis > Report Clock Interaction C, Flow Navigator > Implementation > Report Clock Inte…