当前位置: 首页 > news >正文

Java对象创建过程

前言

在Java开发中,我们经常使用new关键字来创建对象,但你是否想过,当执行Person person = new Person()这行代码时,JVM底层究竟发生了什么?让我们看看对象是怎么被创建的。

对象创建的六个核心步骤

1. 类加载检查

当JVM执行引擎遇到new指令时,首先会进行类加载检查:

Person person = new Person();

JVM会执行以下检查:

  • 在常量池中定位到Person类的符号引用
  • 检查Person类是否已经被加载
  • 检查Person类是否已经被解析
  • 检查Person类是否已经被初始化

如果任何一个步骤没有完成,JVM会先执行相应的类加载过程。

2. 分配内存空间

类加载检查通过后,JVM开始为新对象分配内存。对象所需的内存大小在类加载完成后就已经确定。

2.1 两种分配方式

方式一:指针碰撞

定义:假设Java堆中内存是绝对规整的,所有使用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针为分界点的指示器,分配内存就是把那个指针向空闲空间方向挪动一段与对象大小相等的距离。

方式二:空闲列表

定义:假设Java堆中内存并不是规整的,已被使用的内存和空闲的内存相互交错,虚拟机就必须维护一个列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。

2.2 选择依据

if (垃圾收集器支持压缩整理) {使用指针碰撞方式();
} else {使用空闲列表方式();
}

3. 并发安全处理

对象创建是高频操作,必须保证线程安全。JVM提供两种解决方案:

3.1 CAS + 失败重试

public Object allocateMemory(int size) {do {Object current = heapPointer.get();Object next = current + size;if (heapPointer.compareAndSet(current, next)) {return current;}// CAS失败,重试} while (true);
}

3.2 TLAB

每个线程在Java堆中预先分配一块私有内存区域,避免多线程竞争。

TLAB的优势:

  • 减少线程间同步开销
  • 提高内存分配效率
  • 支持快速的对象分配

4. 内存初始化

public class Person {private String name;    // 初始化为nullprivate int age;        // 初始化为0private boolean active; // 初始化为false
}

JVM将分配的内存空间(除对象头外)全部初始化为零值,这确保了实例字段在未显式赋值时也有确定的初始值。

各类型的零值:

数据类型零值
booleanfalse
byte(byte)0
short(short)0
int0
long0L
float0.0f
double0.0d
char'\u0000'
引用类型null

5. 设置对象头

对象头是JVM管理对象的关键数据结构,包含两部分:

5.1 Mark Word

在64位JVM中,Mark Word占用8字节,存储: 对象哈希码、分代年龄、锁标志位、线程ID、时间戳和偏向锁标志。

Mark Word在不同锁状态下的存储内容:

锁状态25bit31bit1bit4bit1bit2bit
无锁状态unusedhashcodeunused分代年龄001
偏向锁ThreadID(54bit)Epoch(2bit)unused分代年龄101
轻量级锁指向栈中锁记录的指针(62bit)unused分代年龄unused00
重量级锁指向互斥量的指针(62bit)unused分代年龄unused10
GC标记CMS过程用的标记信息(62bit)unused分代年龄unused11

5.2 类型指针

类型指针指向对象的类元数据,JVM通过这个指针确定对象是哪个类的实例。在64位JVM中,类型指针通常占用8字节,但开启压缩指针后可压缩至4字节。

5.3 对象头示例

普通对象的对象头结构:

public class Person {private String name;private int age;
}

对于上述Person对象,其对象头包含:

组成部分大小(64位JVM)内容描述
Mark Word8字节哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳
类型指针4字节(压缩)/8字节指向Person.class的类元数据信息
对齐填充0-7字节确保对象大小为8字节的倍数

数组对象的对象头结构:        

数组对象除了Mark Word和类型指针外,还有额外的4字节存储数组长度:

int[] array = new int[10];
组成部分大小(64位JVM)内容描述
Mark Word8字节对象标记信息
类型指针4字节(压缩)/8字节指向int[]的类元数据
数组长度4字节存储数组的长度(10)
对齐填充根据需要保证对象大小对齐

6. 执行构造函数

public class Person {private String name;private int age;// 编译器生成的<init>方法public Person(String name, int age) {super();           // 调用父类构造器this.name = name;  // 实例字段初始化this.age = age;    // 实例字段初始化// 构造函数体逻辑}
}

构造函数执行的详细过程:

  1. 隐式调用父类构造器:如果没有显式调用super(),编译器会自动添加super()调用
  2. 实例字段初始化:按照在类中声明的顺序执行字段初始化
  3. 执行构造函数体:执行构造函数中的自定义逻辑

 

相关文章:

  • Python-多线程(一)
  • Qwen3与MCP协议:重塑大气科学的智能研究范式
  • JSON基础知识
  • Java框架面试题
  • 【HarmonyOS 5】游戏开发教程
  • 国标GB28181视频平台EasyGBS视频实时监控系统打造换热站全景可视化管理方案
  • 第二章 2.2 数据存储安全风险之数据存储风险分析
  • Mac 双系统
  • 如何在 React 中监听 div 的滚动事件
  • 导出onnx的两种方法
  • 在本地查看服务器上的TensorBoard
  • 2025年- H65-Lc173--347.前k个高频元素(小根堆,堆顶元素是当前堆元素里面最小的)--Java版
  • 洛谷P1591阶乘数码
  • JavaWeb简介
  • 二维 根据矩阵变换计算镜像旋转角度
  • 【LLM大模型技术专题】「入门到精通系列教程」LangChain4j与Spring Boot集成开发实战指南
  • 记录一下centos8安装nginx并部署使用
  • 使用 Python 的 psutil 库进行系统资源监控
  • Apparent connection leak detected问题排查
  • Java Lombok @Data 注解用法详解
  • 做电影网站要很大的主机空间吗/常州网站建设优化
  • 动漫网站开发毕业设计/2022年今天新闻联播
  • wordpress 文本小工具栏/南宁百度seo价格
  • 做网站图片自动切换/网络营销模式有哪几种
  • 网站建设公司联系方式/利尔化学股票股吧
  • 做挂网站吗/谷歌关键词推广怎么做