✡️ 堆中对象创建&布局&访问

吞佛童子2022年6月9日
  • Java
  • JVM
大约 3 分钟

✡️ 堆中对象创建&布局&访问

1. HotSpot 堆中对象的创建 & 分配

  • 限定:
    • 普通 Java 对象
    • 不包括 数组 & Class 对象

1) 类加载

  • 虚拟机拿到一条 new 字节码指令后
  • 去常量池中找到对应类的符号引用
  • 判断符号引用所代表的的类是否已经 加载解析初始化
  • 若没有,则先进行类加载

2) 分配内存

  • 为新生对象分配中内存
  • 对象所需内存大小在类加载后就已经确定
  • 两种方式:
    • 指针碰撞
      • 适用于内存规整
    • 空闲列表
      • 可用于内存不规整的情况
  • 内存是否规整取决于:
    • 垃圾收集器是否具有空间整理功能
  • 如何保证并发下线程安全:
    • CAS + 失败重试
    • 本地线程分配缓冲[TLAB]: 每个线程在堆中预先分配一小块各自内存
      • 是否启用 TLAB 指令:
        • -XX:+/- UseTLAB

3) 初始化 0 值

  • 将分配的内存空间,除对象头外都初始化为 0 值
  • 若使用 TLAB ,也可提前至 TLAB 分配时进行
  • 作用:
    • 保证对象的实例字段在代码中可以不赋初始值就可直接使用

4) 设置对象头

5) 执行构造函数 <init>()

  • <init>() 方法存在于 Class 文件中

2. HotSpot 对象的内存布局

1) 对象头

  1. 存储对象自身的运行时数据 [Mark Word]

    • 数据长度:
      • 32 位虚拟机: 32 bit
      • 64 位虚拟机: 64 bit
    • 动态数据结构,在不同情况下存储不同数据
    • 存放内容:
      • 哈希码
      • 分代年龄
      • 锁的相关信息
  2. 类型指针

    • 确定该对象是哪个类的实例
    • [注:] 非所有虚拟机都会在对象上保留类型指针
    • 8 字节
  3. 数组长度

    • 若为数组,则还需记录数组长度

2) 实例数据

  • 代码中定义的各种类型的字段内容
  • 存储顺序:
    • 虚拟机分配策略参数
      • 默认分配顺序: longs/doubles ints shorts/chars bytes/booleans oops
      • 以上分配前提下,父类变量在子类变量之前
      • +XX:CompactFields == true[default == true] 则允许子类变量插入父类变量的空隙中
    • 源码中字段的定义顺序

3) 对齐填充

  • 起到占位符的作用
  • HotSpot 中要求对象的起始地址必须为 8 字节的整数倍
  • 即,任何对象大小必须为 8 字节的整数倍
  • 对象头已经是 8 字节的整数倍

3. 对象的访问定位

  • 访问方式由虚拟机确定
  • 两种主流方式:
    • 句柄
      • 单独堆中内存部分,用于存放句柄池
      • 句柄中包含实例对象数据 & 类数据各自的地址信息
      • 优点:
        • 在对象被移动时,只需要改变句柄中的对象实例数据指针地址
    • 直接指针
      • 直接存放对象实例数据地址
      • 优点:
        • 只需要一次指针定位,速度更快
      • HotSpot 使用的方式

img.png

img_1.png

上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou