2.1 JVM对象创建

对象创建

本文基于 Hotspot 虚拟机,其他 JVM 行为可能会有区别

本文中的 Eden 区泛指各 GC 中新对象的分配区域,如 G1 的 Eden Region、ZGC 的分配 Page 等

本文行为均基于默认行为,某些 JVM 参数会导致行为不同

对象创建大致可以分为如下几个步骤

复制代码
元数据检查 -> 内存分配 -> 设置对象头 -> 对象内容初始化

详解

通过 new 指令方式创建类实例的流程图如下所示
内存分配



充足
不足

剩余空间小于浪费阈值
剩余空间大于浪费阈值
遇到 new 指令
类元数据已存在?
执行类加载

Loading - Linking - Init
启用 TLAB?
TLAB 余额充足?
TLAB 内分配

无锁指针碰撞
对象大小

vs TLAB剩余空间?
CAS 竞争申请新 TLAB

然后分配
CAS 竞争堆内存

直接分配
空间清零

TLAB预清零 / 分配后清零
设置对象头

MarkWord + KlassPtr
new 指令完成

对象引用入栈
继续执行字节码
invokespecial

触发构造方法
对象真正可用

元数据检查

检查当前命令能否在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否被加载解析和初始化过,如果没有的话会先进行类加载过程。

在元数据中会拿到对象所需内存的大小

内存分配

堆内存格局和 gc 强相关,此处尽量不涉及具体的内存格局细节

堆不是线程独占内存,同时内部对象会频繁的被 gc 清理,因此内存分配大致来说有两个问题,一是分配到哪块内存,二是怎么保证分配的并发安全

指针碰撞

在绝大部分 gc 中,Eden 区都是一块至少局部规整的内存。换句话说有某个地址作为分界点,使用过的内存在一边,空闲的内存在另一边。那么分配内存就只是把分界地址的指针往空闲方向挪动一段与对象所需内存大小相等的距离,这种分配方式就是指针碰撞。

空闲列表

CMS 是非压缩 GC,无法保证堆局部规整,需要维护一个列表记录内存的占用情况来辅助分配对象,这种分配方式就是空闲列表(实际上还是有机制保证局部规整的,但是 CMS 都废弃了就不深入了)

CAS

简单来说就是通过硬件的 CAS 原子操作与自旋实现并发安全,下图是碰撞指针的 CAS 分配示例
CAS 原子操作
准备工作


利用最新 Top

直接重算
开始分配
读取/更新快照

snapshot = 当前 Top
计算期望新指针

new_top = snapshot + size
比较:

内存 Top == snapshot ?
匹配:

Top 更新为 new_top
不匹配:

Top 未修改

获取最新 Top
分配成功

返回 snapshot 地址

TLAB

TLAB(Thread Local Allocation Buffer),简单来说就是给线程预分配一块内存,当新对象可以直接放到 TLAB 中时就可以直接无锁分配。

TLAB 本身的内存申请也是通过 CAS 实现的。

空间清零

Java 语言规范规定的所有字段在未赋值前,必须有默认值,因此对象字段区域需要进行清零操作

这个步骤在流程图上标记在了设置对象头之前,但是实际上不同分配方式该步骤执行时机不同

  1. 堆上直接分配:同步清零
  2. TLAB 分配:TLAB 申请时批量清零整个 TLAB 块

设置对象头

设置对象的元数据

做完这个步骤后在 vm 层面对象就创建完了,只是对象内容还没有进行初始化

对象内容初始化

执行 <init>() 方法初始化对象内容

其他创建方式

除了标准构造(new 或者 反射)外,其他构造方式(克隆、反序列化、Unsafe)流程在在 vm 层面流程极其类似(clone略特殊,他分配内存时不进行空间清零步骤而是直接拷贝内存),只是对象内容初始化方式不同

创建方式 字节码/API 是否调用 <init> 内存初始化方式 内容初始化方式 备注
new 关键字 new 清零 构造方法 最标准的创建方式
反射 Class.newInstance Constructor.newInstance 清零 构造方法 最终调用 new 流程,但在 JVM 层面有安全检查开销
克隆 clone() 内存复制 - 内存初始化完内容也就初始化完了
反序列化 ObjectInputStream.readObject 视情况 清零 序列化流 通常调用第一个不可序列化父类的无参构造
Unsafe Unsafe.allocateInstance 清零 -
相关推荐
辰海Coding1 小时前
MiniSpring框架学习-完成的 IoC 容器
java·spring boot·学习·架构
小小编程路1 小时前
C++ 多线程与并发
java·jvm·c++
AI视觉网奇1 小时前
linux 检索库 判断库是否支持
java·linux·服务器
她的男孩2 小时前
从零搭一个企业后台,为什么我把能力拆成 Starter 和 Plugin
java·后端·架构
RainCity2 小时前
Java Swing 自定义组件库分享(七)
java·笔记·后端
Sam_Deep_Thinking2 小时前
连锁门店的外卖订单平台对接
java·微服务·架构·系统架构
_遥远的救世主_2 小时前
从一次结果集密集型查询 OOM 看 Java 服务的稳定性架构治理
java·后端
一楼的猫2 小时前
从工具链视角对比:番茄作家助手 vs 第三方写作辅助方案
java·服务器·开发语言·前端·学习·chatgpt·ai写作
likerhood3 小时前
Java static 关键字从浅入深
java·开发语言
_院长大人_3 小时前
Java Excel导出:如何实现自定义表头与字段顺序的完全控制
java·开发语言·后端·excel