HotSpot 虚拟机对象探秘

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: HotSpot 虚拟机对象探秘

对象的内存布局

在 HotSpot 虚拟机中,对象的内存布局分为以下 3 块区域:

  • 对象头(Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

image.png

对象头

对象头记录了对象在运行过程中所需要使用的一些数据:

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

对象头可能包含类型指针,通过该指针能确定对象属于哪个类。如果对象是一个数组,那么对象头还会包括数组长度。

实例数据

实例数据部分就是成员变量的值,其中包括父类成员变量和本类成员变量。

对齐填充

用于确保对象的总长度为 8 字节的整数倍。

HotSpot VM 的自动内存管理系统要求对象的大小必须是 8 字节的整数倍。而对象头部分正好是 8 字节的倍数(1 倍或 2 倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

对齐填充并不是必然存在,也没有特别的含义,它仅仅起着占位符的作用。

对象的创建过程

类加载检查

虚拟机在解析.class文件时,若遇到一条 new 指令,首先它会去检查常量池中是否有这个类的符号引用,并且检查这个符号引用所代表的类是否已被加载、解析和初始化过。如果没有,那么必须先执行相应的类加载过程。

为新生对象分配内存

对象所需内存的大小在类加载完成后便可完全确定,接下来从堆中划分一块对应大小的内存空间给新的对象。分配堆中内存有两种方式:

  • 指针碰撞 如果 Java 堆中内存绝对规整(说明采用的是“复制算法”或“标记整理法”),空闲内存和已使用内存中间放着一个指针作为分界点指示器,那么分配内存时只需要把指针向空闲内存挪动一段与对象大小一样的距离,这种分配方式称为“指针碰撞”。
  • 空闲列表 如果 Java 堆中内存并不规整,已使用的内存和空闲内存交错(说明采用的是标记-清除法,有碎片),此时没法简单进行指针碰撞, VM 必须维护一个列表,记录其中哪些内存块空闲可用。分配之时从空闲列表中找到一块足够大的内存空间划分给对象实例。这种方式称为“空闲列表”。

初始化

分配完内存后,为对象中的成员变量赋上初始值,设置对象头信息,调用对象的构造函数方法进行初始化。

至此,整个对象的创建过程就完成了。

对象的访问方式

所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配的。也就是说在建立一个对象时两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。 那么根据引用存放的地址类型的不同,对象有不同的访问方式。

句柄访问方式

堆中需要有一块叫做“句柄池”的内存空间,句柄中包含了对象实例数据与类型数据各自的具体地址信息。

引用类型的变量存放的是该对象的句柄地址(reference)。访问对象时,首先需要通过引用类型的变量找到该对象的句柄,然后根据句柄中对象的地址找到对象。

image.png

直接指针访问方式

引用类型的变量直接存放对象的地址,从而不需要句柄池,通过引用能够直接访问对象。但对象所在的内存空间需要额外的策略存储对象所属的类信息的地址。

image.png

需要说明的是,HotSpot 采用第二种方式,即直接指针方式来访问对象,只需要一次寻址操作,所以在性能上比句柄访问方式快一倍。但像上面所说,它需要额外的策略来存储对象在方法区中类信息的地址。## HotSpot 虚拟机对象探秘

对象的内存布局

在 HotSpot 虚拟机中,对象的内存布局分为以下 3 块区域:

  • 对象头(Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

image.png

对象头

对象头记录了对象在运行过程中所需要使用的一些数据:

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

对象头可能包含类型指针,通过该指针能确定对象属于哪个类。如果对象是一个数组,那么对象头还会包括数组长度。

实例数据

实例数据部分就是成员变量的值,其中包括父类成员变量和本类成员变量。

对齐填充

用于确保对象的总长度为 8 字节的整数倍。

HotSpot VM 的自动内存管理系统要求对象的大小必须是 8 字节的整数倍。而对象头部分正好是 8 字节的倍数(1 倍或 2 倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

对齐填充并不是必然存在,也没有特别的含义,它仅仅起着占位符的作用。

对象的创建过程

类加载检查

虚拟机在解析.class文件时,若遇到一条 new 指令,首先它会去检查常量池中是否有这个类的符号引用,并且检查这个符号引用所代表的类是否已被加载、解析和初始化过。如果没有,那么必须先执行相应的类加载过程。

为新生对象分配内存

对象所需内存的大小在类加载完成后便可完全确定,接下来从堆中划分一块对应大小的内存空间给新的对象。分配堆中内存有两种方式:

  • 指针碰撞 如果 Java 堆中内存绝对规整(说明采用的是“复制算法”或“标记整理法”),空闲内存和已使用内存中间放着一个指针作为分界点指示器,那么分配内存时只需要把指针向空闲内存挪动一段与对象大小一样的距离,这种分配方式称为“指针碰撞”。
  • 空闲列表 如果 Java 堆中内存并不规整,已使用的内存和空闲内存交错(说明采用的是标记-清除法,有碎片),此时没法简单进行指针碰撞, VM 必须维护一个列表,记录其中哪些内存块空闲可用。分配之时从空闲列表中找到一块足够大的内存空间划分给对象实例。这种方式称为“空闲列表”。

初始化

分配完内存后,为对象中的成员变量赋上初始值,设置对象头信息,调用对象的构造函数方法进行初始化。

至此,整个对象的创建过程就完成了。

对象的访问方式

所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配的。也就是说在建立一个对象时两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。 那么根据引用存放的地址类型的不同,对象有不同的访问方式。

句柄访问方式

堆中需要有一块叫做“句柄池”的内存空间,句柄中包含了对象实例数据与类型数据各自的具体地址信息。

引用类型的变量存放的是该对象的句柄地址(reference)。访问对象时,首先需要通过引用类型的变量找到该对象的句柄,然后根据句柄中对象的地址找到对象。

image.png

直接指针访问方式

引用类型的变量直接存放对象的地址,从而不需要句柄池,通过引用能够直接访问对象。但对象所在的内存空间需要额外的策略存储对象所属的类信息的地址。

image.png

需要说明的是,HotSpot 采用第二种方式,即直接指针方式来访问对象,只需要一次寻址操作,所以在性能上比句柄访问方式快一倍。但像上面所说,它需要额外的策略来存储对象在方法区中类信息的地址。

目录
相关文章
|
6月前
|
存储 安全 Java
JavaSE高级篇:HotSpot虚拟机对象探秘
JavaSE高级篇:HotSpot虚拟机对象探秘
|
存储 缓存 算法
JVM第三讲:深入理解java虚拟机之垃圾回收算法?CMS垃圾回收的基本流程?对象引用类型?
JVM第三讲:深入理解java虚拟机之垃圾回收算法?CMS垃圾回收的基本流程?对象引用类型?
222 0
|
27天前
|
存储 缓存 Java
深度解密 Python 虚拟机的执行环境:栈帧对象
深度解密 Python 虚拟机的执行环境:栈帧对象
56 13
|
28天前
|
存储 Python
解密虚拟机的执行环境:栈帧对象
解密虚拟机的执行环境:栈帧对象
21 0
|
5月前
|
算法 Java
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
65 3
|
6月前
|
存储 缓存 安全
深入浅出JVM(三)之HotSpot虚拟机类加载机制
深入浅出JVM(三)之HotSpot虚拟机类加载机制
|
6月前
|
存储 缓存 算法
深入浅出JVM(一)之Hotspot虚拟机中的对象
深入浅出JVM(一)之Hotspot虚拟机中的对象
|
6月前
|
存储 缓存 算法
HotSpot 虚拟机对象存储逻辑
HotSpot 虚拟机对象存储逻辑
58 0
|
13天前
|
安全 虚拟化 数据中心
Xshell 连接 VMware虚拟机操作 截图和使用
Xshell 连接 VMware虚拟机操作 截图和使用
35 4
|
21天前
|
Linux 虚拟化
vmware虚拟机安装2024(超详细)
vmware虚拟机安装2024(超详细)
166 6