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 清零 -
相关推荐
Re.不晚2 小时前
JAVA进阶之路——网络通信的层级密码:Socket切入,理解TCP与HTTP协议
java·tcp/ip·http
Vivienne_ChenW2 小时前
Apollo 配置中心核心用法(实战版)
java·开发语言·分布式·阿里云·产品运营
一灰灰blog2 小时前
Jar包会自己消失?Excel会“记忆“数据?我遇到了两个灵异bug
java·spring boot·bug·excel
计算机毕设指导62 小时前
基于微信小程序的非物质文化遗产推广管理系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven
BYSJMG2 小时前
大数据分析案例:基于大数据的肺癌数据分析与可视化系统
java·大数据·vue.js·python·mysql·数据分析·课程设计
czlczl200209252 小时前
基于 Maven 的多模块项目架构
java·架构·maven
短剑重铸之日2 小时前
《设计模式》第八篇:三大类型之创建型模式
java·后端·设计模式·创建型设计模式
野犬寒鸦3 小时前
从零起步学习并发编程 || 第四章:synchronized底层源码级讲解及项目实战应用案例
java·服务器·开发语言·jvm·后端·学习·面试
!停3 小时前
数据结构二叉树——堆
java·数据结构·算法