Java 中的对象在 JVM 中是怎么映射

369 篇文章 2 订阅
363 篇文章 3 订阅

写在前面

Java 中的对象在 JVM 中是怎么映射的?这个话题一直想写。但是一直没有动笔。后来发现 Java 中的锁很多问题都与这个在 JVM 中映射的对象存在着关系。还是需要搞定它。

我们平时在写 Java 代码的时候,最常见的就是创建一个对象了。这些代码最终都是会在虚拟机上运行的。而一个对象最终在 JVM 中呈现的样子到底是什么呢?还是非常值得我们探究一番。毕竟虚拟机 HotSpot 是 C++ 实现的。

探寻过程

HotSpot 设计的一套 OOP-Klass 模型用来在 JVM 内部进行表示一个对象。这里我们需要提到一个词语 OOP-Klass 二分模型。先从这个单词的含义来理解它。OOP 是 oridinary object pointer,即普通对象指针,它是用来描述对象的实例信息。而 Klass 是代表 Java 类的 C++ 对等体,用来描述 Java 类。

Oops模块可以分为两个独立的部分:OOP 框架与 Klass 框架。

1.探寻 OOP 框架

在 Java 应用程序过程中,每次创建一个 Java 对象的时候,在 JVM 内部也会相应地创建一个 OOP 对象来表示一个 Java 对象。OOPS 类的共同基类为 oopDesc。

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

在 HotSpot 中,根据 JVM 内部使用的对象业务类型,分成了多种 oopDesc 子类。每种类型的 OOP 都代表了一个在 JVM 内部使用的特定对象模型。有点接口和具体实现类的感觉了。

作用
oopDescOOPS 抽象基类
instanceOopDesc描述 Java 类的实例
methodOopDesc描述 Java 方法
constMethodOopDesc描述 Java 方法的只读信息
methodDataOopDesc描述 Java 方法的信息
arrayOopDesc描述数组的抽象基类
objArrayOopDesc描述容纳对象( OOPS ) 元素的数组
typeArrayOopDesc描述容纳基本类型(非 OOPS )的数组
constantPoolOopDesc描述容纳类文件中常量池项的数组
constantPoolCacheOopDesc描述常量池高速缓存
klassOopDesc描述一个 Java 类
markOopDesc描述对象头

当我们在 Java 中使用 new 创建一个 Java 实例对象的时候,JVM 会相应的创建一个instanceOopDesc 对象来表示这个 Java 对象。当我们使用 new 创建一个 Java 数组实例的时候,JVM 就会创建一个 arrayOopDesc 对象来代表这个数组对象。

一个对象在堆内存的存储布局可以分为三个部分,对象头( Header )、实例数据(Instance Data)、和对齐填充( Padding )。而 instanceOopDesc 或者 arrayOopDesc 就是我们提到的对象头。

对象头里面包含了哪些信息呢,它包含了两部分的信息:

  • 一部分是存储对象运行时记录信息,如哈希码( HashCode )、GC 年代分布、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等。很多地方也称这部分为 MarkWord。

  • 另一部分是元数据指针,指向描述类型的 Klass 对象的指针。Klass 对象包含了实例对象所属类型的元数据(meta data)。Java 虚拟机通过这个指针来确定该对象是哪个类的实例。在运行的时候会使用这个指针定位到位于方法区的类型信息。

  • 如果对象是一个 Java 数组的话,那在对象头中还有一块用于记录数组长度的数据。

图片

对象的对齐填充,并不是一定需要存在的。它也没有啥特别大的作用。仅仅是起着占位符的作用。因为 HotSpot 虚拟机要求对象的起始地址必须是 8 字节的整数倍。也即任何对象的大小必须是 8 字节的整数倍。如果不够的时候就需要这个进行补齐。

当我们在 Java 程序里面,使用 new 关键字创建对象的时候,对象的引用保存在栈上,在堆中分配对象实例。这个除了实例数据外,JVM 还会在实例数据的前面自动加上一个对象头 instanceOopDesc 。对象的元数据( instanceKlass )保存在方法区中。

顺便回顾一下方法区是保存什么数据的,方法区是用来存储被虚拟机加载的类型信息、常量、静态常量、即使编译器编译后的代码缓存等数据。

通过栈上引用可以访问到 JVM 内部表示的该对象实例(instanceOop)。当需要调用该类的方法或者访问该类的类变量的时候。就是通过 instanceOop 持有的类元数据指针定位到方法区中的 instanceKlass 对象来完成。

图片

2.探寻 Klass 框架

Klass 数据结构:描述类型自身的布局,以及刻画出于其他类间的关系(父类、子类、兄弟类)。Klass 是一个顶层的基类。

这里主要说一下 instanceKlass, JVM 在运行的时候,为每一个已经加载的 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;
  klassOop        _host_klass;
  objArrayOop     _signers;
  typeArrayOop    _inner_classes;
  klassOop        _implementors 0;
  klassOop        _implementors 1;
  typeArrayOop    _class_annotations;
  objArrayOop     _fields_annotations;
  objArrayOop     _methods_annotations;
  objArrayOop     _methods_parameter_annotations; 
  objArrayOop     _methods_default_annotations;

以上是 OOP 块的内容,在 JVM 中,对象在内存中的基本存在形式就是 OOP。

写在后面

通过以上的探索,基本上有以下的共识。很重要的两个词汇instanceOopDesc、instanceKlass需要在脑海中留下一些印象。

对象头 instanceOopDesc 包含了 MarkWord 与 元数据指针。而 instanceKlass 是用来在JVM 层面表示一个 Java 类的(保存在方法区)。其中元数据指针就是指向 JVM 层表示一个 Java 对象的 instanceKlass 。Klass 对象包含了实例对象所属类型的元数据(meta data)。自然指向它的就被称为了元数据指针了。

MarkWord 里面就包含了与 Java 中锁相关的信息了。这个后面在写一篇专门论述。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源包含源代码 易看易懂 其实就是一发射机制 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。其LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射机制的语言。最近,反射机制也被应用到了视窗系统、操作系统和文件系统。 反射本身并不是一个新概念,它可能会使我们联想到光学的反射概念,尽管计算机科学赋予了反射概念新的含义,但是,从现象上来说,它们确实有某些相通之处,这些有助于我们的理解。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。可以看出,同一般的反射概念相比,计算机科学领域的反射不单单指反射本身,还包括对反射结果所采取的措施。所有采用反射机制的系统(即反射系统)都希望使系统的实现更开放。可以说,实现了反射机制的系统都具有开放性,但具有开放性的系统并不一定采用了反射机制,开放性是反射系统的必要条件。一般来说,反射系统除了满足开放性条件外还必须满足原因连接(Causally-connected)。所谓原因连接是指对反射系统自描述的改变能够立即反映到系统底层的实际状态和行为上的情况,反之亦然。开放性和原因连接是反射系统的两大基本要素。13700863760 Java,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代表链接。反射允许我们在编写与执行时,使我们的程序代码能够接入装载到JVM的类的内部信息,而不是源代码选定的类协作的代码。这使反射成为构建灵活的应用的主要工具。但需注意的是:如果使用不当,反射的成本很高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值