《深入理解Java虚拟机》笔记-HotSpot虚拟机对象探秘

时间:2022-06-06 20:18:15

关于HotSpot虚拟机在Java堆中对象分配、布局、访问的全过程。

1. 对象的创建

虚拟机遇到new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析、初始化过。如果没有,执行相应的类加载过程。

为新生对象分配内存。对象所需内存大小在类加载完成后就能确定。分配内存的方式取决于Java堆是否规整,也就是取决于所采用的的垃圾收集器是否带有压缩整理功能。在使用Serial、ParNew等带有Compact过程的收集器时,采用指针碰撞分配算法;在使用CMS等基于Mark-Sweep算法收集器时,采用空闲列表。

为了保证分配内存时线程安全,可以把内存分配的动作按照线程划分在不同的空间进行。每个线程在Java堆中预先分配一小块内存——本地线程分配缓冲(TLAB),只有TLAB用完需要分配新的TLAB时,才需要同步锁定。

内存分配完成后,虚拟机将分配到的内存空间初始化为零值。

对对象进行必要的设置,哪个类的实例,哈希码,GC分代年龄信息等。

2. 对象的内存布局

对象在内存中的布局分为3块区域:对象头、实例数据、对齐填充。

对象头包括:用于存储对象自身的运行时数据(哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳)和类型指针(指向类元数据的指针,虚拟机通过它确定这个对象是哪个类的实例)。

实例数据:程序中定义的各种类型的字段内容。包含从父类继承下来的,子类中定义的。相同宽度的字段总是被分配到一起。父类定义的变量出现在子类之前。如果CompactFields为true,那么子类中较窄的变量也可能会插入到父类变量的空隙中。

对齐填充:对象的大小必须是8的整数倍。如果对象实例数据部分没有对齐时,需要通过对其填充来补全。

3. 对象的访问定位

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

使用句柄:Java堆中划出一块内存作为句柄池。reference中保存的是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。

使用直接指针:reference中存储的直接就是对象地址,Java堆对象的布局中要考虑如何防止访问类型数据的相关信息。