企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 对象的内存布局 在HotSpot虚拟机中,对象的内存布局分为以下3个区域: * 对象头 * 实例数据 * 对齐填充 ![](https://img.kancloud.cn/ce/c1/cec1a91e96673b1c679c0b6562b621f8_1938x1098.png) ### 对象头 对象头包含Mark Word和类型指针2个部分。如果是数组对象,则有一部分存储数组的长度。 Mark Word中存储: * 哈希吗(HashCode) * GC分代年龄 * 锁状态标志 * 线程持有的锁 * 偏向线程ID * 偏向时间戳 ### 类型指针 类型指针Klass存储该对象的类对象的内存地址,可以通过该指针知道对象是属于哪个类。 ### 实例数据 实例数据部分就是成员变量的值,其中包括父类成员变量和本类成员变量。 ### 对齐填充 保证对象内存大小是8字节的倍数,不足的部分按填充对齐。 ## 参考资料 * 周志明 * 《深入理解Java虚拟机》 ## 对象的创建过程 ![](https://img.kancloud.cn/db/e3/dbe3ad439a073dbb1b532a3444320af4_1452x1178.png) 1. 类加载检查 先检查对象的类是否加载,没有加载则先加载类。(类加载完成后就能计算出对象的大小,请参考[Java如何计算对象大小](%E5%A6%82%E4%BD%95%E8%AE%A1%E7%AE%97%E5%AF%B9%E8%B1%A1%E5%A4%A7%E5%B0%8F.md) 2. 内存分配 给对象分配内存,如果使用TLAB,则优先在TLAB空间分配,再在Eden区分配。如果内存规整则使用指针碰撞方式分配内存,不规整则使用空闲列表方式分配内存。 * 指针碰撞 内存规整(说明垃圾回收采用的是标记整理算法),已使用内存和空闲内存分界线上放着一个指针左为分界指示器。只需要将指针移动往空闲内存一端移动对象大小的距离。 * 空闲列表 内存不规整(说明垃圾回收采用的是标记清除算法,有碎片),那么VM必须维护一个列表,记录哪些内存是空闲可用的,分配时从空闲列表中找到一块足够大的内存给对象示例使用。 3. 初始化 * 设置零值 内存分配完成后,虚拟机会将分配到内存的空间都初始化为零值。(不包含对象头) 这也保证了Java代码不赋值初始化就直接使用,程序能访问到这些字段的数据类型对应的零值。 * 设置对象头信息 -- 设置klass指针为对应类对象的地址 -- 对象的hashCode -- 对象的分代年龄 等 * 执行构造方法`<init>` 执行对象所属类定义的`init`构造方法,完成初始化。 至此,一个可用的对象才算是完全创建出来。 ## 对象的访问方式 对象的存储空间是在堆中分配的,但是这个对象的引用(存的是地址)却是在栈中分配的。 Java程序通过栈上的`reference`引用来操作堆上的具体对象。 目前对象的访问方式有句柄访问和直接指针访问方式。 ### 句柄访问方式 ![](https://img.kancloud.cn/80/ba/80baa5cad3929ee49b449d6b042938a3_1370x702.png) 对象引用内存储的是指向句柄地址,句柄对象中包含对象实例数据指针和类型数据指针。 ### 直接指针访问方式 ![](https://img.kancloud.cn/55/fa/55fa4fdd18191458b964019ddf68f906_1362x704.png) 对象引用中存储的是对象实例数据指针,再通过对象头里的klass指针找到对象类型数据。 总结: 使用句柄方式访问,优点是对象变化时栈中的reference的值不用改变,存储的是稳定的句柄地址; 而使用直接指针方式访问最大好处就是速度更快,它节省了一次指针定位的时间开销。 HotSpot虚拟机采用直接指针的对象访问方式。