OR博客
HotSpot虚拟机Java堆中对象的分配、布局和访问
苗锦洲
创建于:2021-09-24 22:54:44
0
33
226
0
了解Java运行时数据区后,还需了解对象在Java堆中是如何分配、布局和访问的,以普通Java对象为例,不包括数组和Class对象。

了解 Java 运行时数据区后,还需了解对象在 Java 堆中是如何分配、布局和访问的,以普通 Java 对象为例,不包括数组和 Class 对象。

HotSpot 虚拟机 Java 堆中对象的分配、布局和访问

0. 前提

  • new 关键字,排除复制、反序列化
  • 普通 Java 对象,排除数组和 Class 对象

1. 分配过程

1. 检查 new 指令的参数是否能在常量池中找到一个类的符号引用,并且检查是否已被加载、解析和初始化过

  • 否则执行类加载过程

2. 为新生对象分配内存

  • 如何分配

    • 根据所用的垃圾回收器是否有空间压缩整理功能决定

    • 方式

      • 指针碰撞
        Bump The Pointer

        • 内存空间是规整的,一边正在使用的,一边空闲的,边界处有一个指针,分配内存只是把指针往空闲的那部分移动与对象大小相等的距离
      • 空闲列表
        Free List

        • 不是规整的,分配的时候在空闲列表上找到一块足够大的空间,更新列表记录
  • 考虑高并发

    • 方案一

      • 分配内存进行同步处理:CAS+ 失败重试保证更新操作的原子性

        • Atomic::cmpxchg_ptr()
          goto retry
    • 方案二

      • 每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲区(Thread Local Allocation Buffer,TLAB),TLAB 用完后才会同步锁分配内存
      • 是否启用 TLAB,可以通过-XX:+/-UseTLAB 指定

3. 零值初始化

  • 不包括对象头
  • 如果使用 TLAB 可以提前到 TLAB 分配时顺便进行
  • 保证对象实例字段在 Java 代码中不赋初始值就能直接使用,使程序能访问到字段得数据类型对应的零值

4. 对象头 Object Header 初始化

  • 根据虚拟机当前运行状态的不同,会有不同的设置方式

  • 对象头内容

    • 对象是哪个类的实例
    • 如何找到类的元数据信息
    • 对象的哈希码
    • 对象的 GC 分代年龄等信息
    • ......

5. 执行构造函数

  • 一般来说会执行 Class 文件中的()方法

  • 父类子类执行顺序

    • 父类静态代码块
    • 子类静态代码块
    • 父类代码块
    • 父类构造函数
    • 子类代码块
    • 子类构造函数

2. 对象内存布局

对象头 Header

  • Mark Word

  • 类型指针 Class Pointer

    • 指向类型元数据的指针
    • 虚拟机通常用这个来确定对象是哪个类的实例
  • 数组长度 length

    • 如果是数组的话才需要
    • 普通 Java 对象可以直接确定大小,数组长度不固定的话,没法只通过元数据推断出数组大小

实例数据 Instance Data

  • 保存对象的所有字段,不管是父类还是子类

    • 默认顺序:
      long/double, int, short, char, byte/boolean, oop (Ordinary Object Pointers)
  • 存储顺序一般是长度一样的存一起,与代码中定义的顺序也有关系

    • -XX:FieldsAllocationStyle

    • +XX:CompactFileds,默认为 true

      • 允许子类中比较窄的变量插入到父类变量的空隙之中,节省一点点空间

对齐填充 Padding

  • HotSpot 规定任何对象大小都必须是 8 字节的整数倍,对象头已经设计为 8 字节的整数倍,如果实例数据部分没有对齐的话需要通过对齐填充补全

3. 对象访问

通过 Java 栈(本地变量表)上的 reference 数据来操作对上的具体对象,由虚拟机实现具体方式

主流方式

  • 句柄

    • reference 存储的是对象实例数据和对象类型数据
    • Java 堆中单独开辟一块区域存放句柄,访问对象实例数据需要两次跳转
  • 直接指针

    • reference 存储的直接就是是对象地址,可以直接访问到对象示例数据,访问对象类型数据才需要两次跳转
    • 因为通常只是访问对象,而不是访问对象的类型数据

补充

查看字节码的插件

  • jclasslib

对象类型数据

  • 对象类型数据就是被虚拟机加载的类信息(即 Class 信息,见 2.2.5 方法区)

对象实例数据

  • 对象实例数据就是被 new 出来的对象信息。
评论
楼主暂时不想被别人评论哦~