从小工到专家的 Java 进阶之旅:HotSpot虚拟机对象探秘

开发 前端
Java 堆中将可能会划分出一块内存来作为句柄池,reference 中存储的是对象的句柄地址,句柄中包含了对象实例数据和类型数据各自的具体地址信息。好处是解耦,reference 中存储的是稳定句柄地址,在对象被移动(垃圾回收等)时只会改变句柄中实例数据指针,而 reference 本身不需要修改。

今天我们一起看一下HotSpot虚拟机中的对象。

对象的创建(以 new 关键字为例)

创建过程

  1. Java 虚拟机遇到字节码new指令,首先检查new指令参数是否能够在常量池中定位到一个类的符号引用

如果是,继续下一步

如果否,执行类加载过程

如果是,检查这个符号引用代表的类是否已经被加载、解析和初始化

如果否,执行类加载

  1. 虚拟机为新生对象分配内存(对象所需内存大小在类加载完成后即可确定)
  2. 虚拟机将分配的内存空间(不包括对象头)初始化为零值。
  3. 虚拟机对对象进行必要设置,比如设置对象头信息:
  4. 对象是哪个类的实例
  5. 如何找到类的元数据信息
  6. 对象的哈希码
  7. 对象的 GC 分代年龄
  8. new指令之后会接着执行<init>()方法,按照程序员意愿初始化对象

内存分配

  1. 内存分配算法:拟机为新生对象分配内存有指针碰撞和空闲列表两种方式,具体选择哪种,取决于垃圾收集器是否带有空间压缩整理的能力。Serial、ParNew 带压缩整理,采用指针碰撞;CMS 基于清除算法,采用空闲列表。

指针碰撞 (Bump The Pointer):堆内存绝对规整,已使用的在一边,未使用的在另外一遍,中间通过指针作为分界点指示器,分配内存即移动指针。

空闲列表 (Free List):堆内存不规整,已使用与未使用相互交错,需要维护一个列表,记录哪些内存块可用,分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表记录。

  1. 线程安全问题:创建对象比较频繁,需要保证线程安全,避免多个对象分配了相同的内存区域,一般是两种方式:

同步处理:虚拟机采用CAS+失败重试方式保证更新操作的原子性

本地线程分配缓冲:把内存分配的动作按照线程划分在不同空间之中进行,即每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲 (Thread Local Allocation Buffer, TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲缓冲区用完了,分配新的缓冲区时才需要同步锁定。是否使用 TLAB,可以通过参数-XX:+/-UseTLAB参数设定。

对象的内存布局

  1. 对象头 (Header)

用于存储对象自身运行时数据:哈希码 (Hash Code)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等,长度在 32 位和 64 位虚拟机分别是 32 比特和 64 比特,官方称为 Mark Word。

类型指针,即对象指向它的类型元数据指针,Java 虚拟机通过这个指针来确定该对象是哪个类的实例。

如果对象是数组,还有一个数据记录数组长度

  1. 实例数据 (Instance Data):即程序代码里面定义的各种类型的字段内容。存储顺序受虚拟机分配策略 (-XX:FieldsAllocationStyle 参数)和字段在 Java 源码中定义顺序影响。HotSpot 虚拟机默认分配顺序为 longs/doubles、ints、shorts/charts、bytes/booleans、oops(Ordinary Object Pointers, OOPS),即相同宽度字段被分配到一起存放,在满足这个前提条件情况下,在父类中定义的变了会出现在子类之前。如果 HotSpot 虚拟机的+XX:CompactFields 参数设置为 true,子类中较窄的变量也允许插入父类变量的空隙中,以节省空间。
  2. 对齐填充 (Padding):占位符作用。HotSpot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍。

对象的访问定位

Java 程序会通过栈上的 reference 数据来操作堆上的具体对象,主流的访问方式主要有使用句柄和直接指针两种:

  • 使用句柄:Java 堆中将可能会划分出一块内存来作为句柄池,reference 中存储的是对象的句柄地址,句柄中包含了对象实例数据和类型数据各自的具体地址信息。好处是解耦,reference 中存储的是稳定句柄地址,在对象被移动(垃圾回收等)时只会改变句柄中实例数据指针,而 reference 本身不需要修改。
  • 直接指针:Java 堆中的对象布局必须考虑如何放置访问类型数据的相关信息,reference 中存储的是对象地址。好处是速度快,节省一次指针定位时间开销,HotSpot 主要使用直接指针。

使用句柄使用句柄

直接指针直接指针


责任编辑:武晓燕 来源: 看山的小屋
相关推荐

2018-06-29 14:48:24

数据库MySQL必读书籍

2019-11-13 14:55:16

小工Linux专家

2010-11-05 09:47:11

OracleJava虚拟机

2012-08-06 09:26:19

Java虚拟机垃圾回收

2021-09-10 00:34:22

Java 线程启动

2020-09-02 07:03:04

虚拟机HotSpotJava

2018-07-25 14:41:29

Java虚拟机Android

2014-02-21 11:20:34

KVMXen虚拟机

2018-10-25 09:04:56

Java虚拟机JVM

2017-07-11 09:35:10

大数据linuxjava

2012-05-18 10:22:23

2011-06-22 13:35:55

JVM

2018-06-19 15:39:21

HeapJava虚拟机

2014-12-18 09:41:44

虚拟化迁移

2020-01-17 10:52:37

无服务器容器技术

2009-08-28 11:54:27

VMware虚拟机

2009-06-12 16:02:58

装载Java虚拟机

2010-09-17 15:12:57

JVMJava虚拟机

2010-07-26 09:02:38

2013-07-17 09:32:58

点赞
收藏

51CTO技术栈公众号