对象的内部结构、内存布局和访问定位

对象的内部结构、内存布局和访问定位,第1张

对象的内部结构、内存布局和访问定位

学习宋红康老师的JVM课程已经有一段时间了,学习过程中发现,有些内容遗忘得很快,虽然自己也用印象笔记记录了,但是如果没有输出,知识仍然不能完全地消化。因此,决定在JVM专栏中记录和总结学过的内容,欢迎大家在评论区交流。

文章目录
  • 前言
  • 1. 对象的内存布局
    • 1.1 对象头
    • 1.2. 示例数据
    • 1.3. 对齐填充padding
  • 2、对象的访问定位
  • 总结


前言

对象在内存中是怎么布局的?”
“对象头具体包括什么?”
“锁在对象的哪里?”
“对象怎么定位?”
面试过程中,你是否被问到过这些问题,如果不能完全回答出来,那么这篇文章一定要看下去。

1. 对象的内存布局

对象实例中主要包含三部分的内容,分别为对象头、示例数据和对齐填充padding。下面分别进行介绍。

1.1 对象头

对象头中的内容主要是运行时元数据和类型指针。
其中运行时元数据主要存储的是以下六个内容:

  • 哈希值
  • GC分代年龄
  • 锁状态标志
  • 线程持有的锁
  • 偏向线程ID
  • 偏向时间戳

所以我们知道,锁是在对象头中的。
那么类型指针又是什么呢?实际上,类型指针是指向元数据类型的InstanceKlass,用来确定该对象所属的类型。比如通过getInstance()得到对象实例时就需要用到该指针。这里需要注意的是,不是所有对象都会保留该指针的。
以上我们说的对象指的是自定义对象,如果是数组对象,那么还需要记录数据的长度。

1.2. 示例数据

示例数据中是对象真正存储的有效信息,包括程序中定义的各种字段,这里的字段也包括从父类、Object类中定义的各种字段。
那么这么多字段,是怎么放在一起的呢?所以就不得不提存放规则了。示例数据的存放规则遵循以下三个原则:

  • 父类中定义的变量在子类之前。
  • 同一个类中,相同宽度的字段被分配在一起。如四个字节的数据一起。
  • 若CompareFileds参数为true(默认为true),子类的窄变量可能插入到父类变量的空隙。
1.3. 对齐填充padding
  • 由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。
  • 总的一句话来说,“数据项仅仅能存储在地址是数据项大小的整数倍的内存位置上(分别为偶地址、被4整除的地址、被8整除的地址)”比如int类型占用4个字节,地址仅仅能在0,4,8等位置上。
  • 想讲好对齐填充,其实还是得去了解c++实现,这里不过多介绍。

2、对象的访问定位

堆中的对象是通过元数据指针找到方法区中的元数据信息的,那么栈帧中的对象引用又是如何访问找到对应的对象呢?

对象的访问定位方式主要有两种:

  • 句柄访问
  • 直接访问

句柄访问方式:
堆区中开辟了一块空间用于存储句柄,该空间称为句柄池。句柄池分别存储着两个句柄,分别是到对象实例数据的指针和到对象类型数据的指针。前者指向对象实例数据,后者指向对象类型数据。栈帧的局部变量表中存储着到对象实例数据指针的位置,即指向句柄池中的第一个句柄。

直接访问方式:
直接访问方式相比句柄访问就简单多了,局部变量表中直接存储着对象实例数据的地址,而对象实例数据存储着方法区中对象类型数据的地址。这也是Hotspot虚拟机中的访问方式。

总结

关于对象的内部结构、内存布局和访问定位,值得深入的点还有很多,这里只是做一个初步的介绍,待深入学习后,会及时更新相应的内容。


参考资料:

https://juejin.cn/post/6935627192218943501
尚硅谷宋红康JVM全套教程

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5696650.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存