OR博客
HotSpot虚拟机Java堆中对象的分配、布局和访问
OrdinaryRoad
创建于:2021-09-24 22:54:44
0
17
57
0
了解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文件中的<init>()方法 - 父类子类执行顺序 - 父类静态代码块 - 子类静态代码块 - 父类代码块 - 父类构造函数 - 子类代码块 - 子类构造函数 ## 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出来的对象信息。
评论
楼主暂时不想被别人评论哦~