JVM 对象的内存分配和访问

对象的实例化方式

  • new
  • Class#newInstance()
  • clone()
  • 反序列化

对象的创建过程

在这里插入图片描述

  1. 虚拟机收到new指令触发。
  2. 类加载检查:首先检查该类是否被加载。如果没有加载,则进行类的加载过程;如果已经加载,则在堆中分配内存。对象所需的内存的大小在类加载完成后便可以完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。这个指令完毕后,将指向实例对象的引用变量压入虚拟机栈栈顶。
  3. 对象分配内存:从堆中划分出一块确定大小的内存。
  4. 内存空间初始化:内存分配完后,虚拟机需要将分配到的内存空间初始化为零值(如:int值为0,boolean值为false等),保证了对象的实例字段在Java代码中可以直接使用。
  5. 为对象进行必要的设置:虚拟机为对象进行设置,如设置对象属于哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,这些信息存放在对象头中。
  6. 从虚拟机的角度来看,一个新的对象已经创建完毕。但从Java程序的角度来看,对象创建才刚开始,所有的字段还是零值,所以需要程序员进行初始化操作,这样一个真正可用的对象才算完全产生出来。

类加载检查

  • 检查 new 指令单参数是否能在常量池中定位到一个类的符号引用。
  • 检查类是否被加载、解析、初始化过。

对象内存分配方式

虚拟机为新对象分配内存,从堆中划出一块确定大小的内存,因为对象所需内存的大小在类加载完后可以完全确定。

堆内存是否规整:

在这里插入图片描述

  • 堆内存规整:已使用的内存在一边,未使用内存在另一边。
  • 堆内存不规整:已使用内存和未使用相互交错。

堆内存是否规整是由垃圾收集器是否带有压缩整理功能决定的。

内存分配方式:

分配方式的选择 取决于 Java堆内存是否规整:

  • 指针碰撞方式:
    • 堆内存绝对规整。
    • 分配过程:将已使用内存和为使用内存之间放一个分界点的指针,分配内存时,指针会向未使用内存方向移动,移动一段与对象大小相等的距离。
      在这里插入图片描述
  • 空闲列表:
    • 堆内存不规整。
    • 分配过程:虚拟机内部维护了一个记录可用内存块的列表,在分配时从列表找一块足够大的空间划分给对象实例,并更新列表上的记录。

Java堆是否规整 由所采用的垃圾收集器是否带有压缩整理功能决定

线程安全问题

对象创建在虚拟机中是非常频繁的操作,即使仅仅修改一个指针所指向的位置,在并发情况下也会引起线程不安全。

解决线程安全问题有两种方案:

  • 同步处理分配内存空间的行为。虚拟机采用 CAS + 失败重试的方式 保证更新操作的原子性。
  • 把内存分配行为 按照线程 划分在不同的内存空间进行。本地线程分配缓冲(Thread Local Allocation Buffer ,TLAB)技术。

对象的内存布局

Java虚拟机(HotSpot)中,对象在 Java 内存中的 存储布局 可分为三块:

  1. 对象头 存储区域
  2. 实例数据 存储区域
  3. 对齐填充 存储区域

在这里插入图片描述

对象头区域:

  • 存储对象自身的运行时数据,如:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳。
  • 存储对象类型指针,即对象指向类元数据的指针,JVM可以确定这个对象属于哪个类的实例。
  • 如果是数组,对象头中还有一块记录数组长度的数据。

实例数据区域:

  • 代码中定义的字段内容。

对齐填充区域:

  • 占位符。
  • 非必须。

说明:占位符起占位作用,因为对象的大小必须是8字节的整数倍,而因HotSpot VM的要求对象起始地址必须是8字节的整数倍,且对象头部分正好是8字节的倍数。因此,当对象实例数据部分没有对齐时(即对象的大小不是8字节的整数倍),就需要通过对齐填充来补全。

对象的访问定位

创建对象后,Java程序需要通过栈上的reference数据访问堆中的对象实例。

目前对象访问方式有两种:

  • 句柄访问
  • 直接指针访问

目前大部分JVM虚拟机,包括HotSpot虚拟机采用的是直接指针访问。

句柄访问

在这里插入图片描述

说明:

  • 在Java堆中划分一块内存作为句柄池。
  • 栈中reference数据存储的是对象的句柄地址。
  • 句柄中包含对象实例数据和类型数据的地址。

优点:

  • reference存储的稳定的句柄地址,在对象被移动时只会改变句柄中实例数据指针,reference不需要做修改。

场景:

  • 适合频繁移动对象地址。

直接指针访问

在这里插入图片描述

说明:

  • 栈中reference数据存储的是对象地址。
  • 对象类型数据的指针直接存放在对象中的对象头。

优点:

  • 速度快。

场景:

  • 适合频繁访问对象。
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐