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

Java 创建对象过程 JVM 内存分配并发安全笔记

一、Java 创建对象的完整过程

Java 中通过 new 创建对象,其背后是 JVM 的一系列操作,主要包括:

1.1 类加载检查

当代码中出现 new 指令时,JVM 首先会检查这个类是否已经被加载、解析和初始化过。

  • 若未加载,会触发类加载过程(ClassLoader)。
  • 加载后才可以为该类分配内存。

1.2 分配内存

为新对象分配内存空间(位于堆内存),有两种方式:

分配方式说明
指针碰撞(Bump-the-pointer)如果内存是规整的(使用 Serial、ParNew 等),通过一个指针一直向后分配即可。
空闲列表(Free-list)若堆空间不规整(比如 CMS),需要维护一个可用内存块的列表,找到合适位置进行分配。

1.3 并发安全保障(重点)

在多线程环境下,为对象分配内存可能出现竞争问题(多个线程争抢分配内存)。

JVM 采用了以下几种机制来保证内存分配的并发安全:

1)CAS + 失败重试
  • 指针碰撞方式下,使用 CAS(Compare-And-Swap)操作更新指针;
  • 若失败(因为有其他线程成功更新了),则重试,直到成功。
2)Tlab(Thread Local Allocation Buffer)线程本地分配缓冲区
  • 每个线程会在堆中分配一块小内存作为私有区域,称为 TLAB;
  • 在 TLAB 中分配对象几乎不需要加锁,效率高;
  • TLAB 用完再申请,或者直接走共享堆的 CAS 方式。

-XX:+UseTLAB 参数用于开启 TLAB(默认开启)。


1.4 对象初始化

JVM 完成内存分配后,会进行以下操作:

① 将分配的内存空间清零(不包括对象头)
② 设置对象头(包括哈希码、GC 分代年龄、类型指针等)
③ 执行 <init> 构造方法初始化对象内容

二、JVM 对象分配时的内存结构

对象内存结构包含:

  1. 对象头(Header)
    • Mark Word:存储哈希值、GC 信息、锁状态等;
    • Class Pointer:指向对象的类型元数据(即 class 对象);
  2. 实例数据(Instance Data)
    • 包含类中定义的字段内容;
  3. 对齐填充(Padding)
    • 为了满足内存对齐要求(通常是8字节倍数)。

三、对象创建的 JVM 字节码体现

使用 javap -v 类名.class 查看,可以发现:

new com.example.User
invokespecial #构造方法

其中:

  • new:分配内存并创建引用;
  • dup:复制栈顶引用;
  • invokespecial:调用构造方法初始化对象。

四、对象创建方式总结

方式是否走构造函数是否可控
new✅ 是
Class.newInstance()✅ 是❌(必须有无参构造)
Constructor.newInstance()✅ 是✅(可选构造)
clone()❌ 否(浅拷贝)
反序列化❌ 否(自动恢复)

五、拓展:逃逸分析与栈上分配

配合 JIT 编译器优化,对未逃逸的对象可优化为栈上分配,从而避免堆分配和 GC。

需开启参数:

-XX:+DoEscapeAnalysis
-XX:+EliminateAllocations

六、面试高频问题总结

Q1:Java 中 new 一个对象经历了哪些步骤?

  • 类加载检查;
  • 内存分配(TLAB/CAS);
  • 对象头初始化;
  • 构造函数初始化。

Q2:JVM 如何保证对象分配的并发安全?

  • TLAB 本地分配;
  • CAS 保证共享分配指针的原子性。

Q3:对象是一定分配在堆上吗?

不是。如果开启逃逸分析、对象未逃逸,有可能在栈上分配,提高效率。


参考

《深入理解Java虚拟机》 第三版 - 周志明
https://book.douban.com/subject/34907497/

OpenJDK 官方文档:对象分配与内存布局
https://openjdk.org/

TLAB 配置详解(Oracle 官方)
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

Java 内存模型 JSR-133 规范
https://jcp.org/en/jsr/detail?id=133

JVM 源码阅读参考(HotSpot)
https://github.com/openjdk/jdk

http://www.lryc.cn/news/581190.html

相关文章:

  • 介绍Flutter
  • 2025最新软件测试面试八股文
  • 在SoC数据加解密验证中使用 Python 的 gmssl 库
  • 【论文笔记】OctoThinker:突破 Llama 推理瓶颈的中期训练范式
  • web前端面试-- MVC、MVP、MVVM 架构模式对比
  • 硬件嵌入式工程师学习路线终极总结(二):Makefile用法及变量——你的项目“自动化指挥官”!
  • WEB攻防-文件包含LFIRFI伪协议编码算法无文件利用黑白盒
  • Go语言的web框架--gin
  • NX二次开发——NX二次开发-检查点是否在面上或者体上
  • MyChrome.exe与Selenium联动避坑指南:User Data目录冲突解决方案
  • 一篇文章快速入门TypeScript基础语法
  • 超详细yolov8/11-segment实例分割全流程概述:配置环境、数据标注、训练、验证/预测、onnx部署(c++/python)详解
  • Zigbee/Thread
  • Xshell使用技巧
  • 七牛云前端面试题及参考答案 (上)
  • 2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--下篇
  • C语言socket编程-补充
  • 测试时学习(TTT):打破传统推理界限的动态学习革命
  • vue router 里push方法重写为什么要重绑定this
  • JVM与JMM
  • RAL-2025 | 清华大学数字孪生驱动的机器人视觉导航!VR-Robo:面向视觉机器人导航与运动的现实-模拟-现实框架
  • rpgmaker android js常用属性解析
  • UI前端大数据可视化实战:如何设计高效的数据交互界面?
  • FLAN-T5:规模化指令微调的语言模型
  • 职坐标:AI图像识别NLP推荐算法实战
  • 【学习笔记】MySQL技术内幕InnoDB存储引擎——第5章 索引与算法
  • 针对工业触摸屏维修的系统指南和资源获取途径
  • Spring Bean 控制销毁顺序的方法总结
  • 408第三季part2 - 计算机网络 - 计算机网络分层结构
  • 【性能优化与架构调优(二)】高性能数据库设计与优化