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

JVM对象创建全过程

JVM对象创建全过程深度解析

1. 对象创建的整体流程

JVM创建对象的过程可以分为7个关键步骤,从类检查到内存分配,再到对象初始化:

类加载检查 → 内存分配 → 内存空间初始化 → 对象头设置 → 构造函数执行 → 栈帧引用建立 → 对象使用

2. 详细创建步骤

2.1 类加载检查

  • 检查时机:遇到new指令时
  • 检查内容
    • 类是否已加载、解析和初始化
    • 未加载则执行类加载过程
  • 异常NoClassDefFoundError(类找不到)或ClassNotFoundException

2.2 内存分配

分配方式
分配方式原理适用场景
指针碰撞通过指针移动分配连续内存堆内存规整(Serial/ParNew)
空闲列表维护可用内存块列表分配堆内存不规整(CMS)
TLAB线程私有分配缓冲区(Thread Local Allocation Buffer)多线程环境减少竞争
内存分配关键参数
-XX:+UseTLAB              # 启用TLAB(默认开启)
-XX:TLABSize=512k         # 设置TLAB大小
-XX:+PrintTLAB            # 打印TLAB分配信息

2.3 内存空间初始化

  • 清零操作:将分配的内存空间初始化为零值
    • 数值类型:0
    • 布尔类型:false
    • 引用类型:null
  • 目的:保证对象字段不包含随机值

2.4 对象头设置

对象头包含三部分信息(以64位JVM为例):

  1. Mark Word(8字节):

    • 哈希码
    • GC分代年龄
    • 锁状态标志
    // HotSpot源码中的markOop定义
    union markOop {uintptr_t value;struct {uintptr_t locked:1;       // 锁标志位uintptr_t age:4;          // 分代年龄uintptr_t hash:31;        // 哈希码} bits;
    };
    
  2. Klass Pointer(4字节,压缩开启时):

    • 指向方法区的类元数据
  3. 数组长度(仅数组对象有,4字节)

2.5 实例数据填充

  • 按照字段类型和声明顺序存储

  • 字段对齐(通常按8字节对齐)

  • 字段重排序优化示例:

    class Reordered {byte b;     // 1字节long l;     // 8字节int i;      // 4字节
    }
    // 优化后内存布局:[long][int][byte]+3字节填充
    

2.6 构造函数执行

  • 执行方法(非
  • 初始化顺序:
    1. 父类构造器
    2. 实例变量初始化
    3. 构造器代码块

3. 对象内存布局示例

Object obj = new Object()为例(64位JVM开启压缩指针):

[对象头][Mark Word(8字节)]      : 0x0000000000000001 (无锁状态)[Klass Pointer(4字节)]   : 0x0000000100000000
[实例数据]                 : 无(Object类无实例字段)
[对齐填充(4字节)]          : 0x00000000
总大小:16字节

4. 对象访问定位方式

4.1 句柄访问

  • 原理:堆中维护句柄池,包含对象实例数据和类型数据指针
  • 优点:引用稳定(对象移动时只需更新句柄)
  • 缺点:多一次指针跳转

4.2 直接指针(HotSpot采用)

  • 原理:引用直接指向堆中对象
  • 优点:访问速度快(少一次指针跳转)
  • 缺点:对象移动时需要更新所有引用

5. 对象创建的性能优化

5.1 TLAB优化

  • 原理:每个线程预先分配一小块内存(默认占Eden区1%)

  • 查看TLAB使用

    jstat -gc <pid> | grep TLAB
    

5.2 逃逸分析与栈上分配

  • 优化条件

    • 对象未逃逸出方法
    • 支持标量替换(-XX:+EliminateAllocations)
  • 示例

    public void test() {User user = new User();  // 可能直接在栈上分配user.id = 1;System.out.println(user.id);
    }
    

6. 对象创建的字节码分析


// 源代码
Object obj = new Object();// 对应字节码
0: new           #2      // ① 创建对象
3: dup                 // ② 复制引用
4: invokespecial #1     // ③ 调用<init>
7: astore_1            // ④ 存储引用
  1. new:创建未初始化对象
  2. dup:复制引用(用于后续初始化和方法调用)
  3. invokespecial:调用构造函数
  4. astore:将引用存入局部变量表

7. 常见面试问题

7.1 new关键字背后发生了什么?

  • 类加载检查 → 内存分配 → 初始化 → 构造方法调用 → 返回引用

7.2 对象一定在堆上分配吗?

  • 不一定,可能通过栈上分配标量替换优化

7.3 如何证明对象内存布局?

  • 使用JOL工具:

    System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    

8. 生产环境建议

  1. 监控对象创建速率

    jstat -gcutil <pid> 1000
    
  2. 优化小对象分配

    • 避免过度包装
    • 考虑对象复用(享元模式)
  3. 合理设置堆大小

    -Xms4g -Xmx4g -XX:NewRatio=2
    

相关文章:

  • .net core 项目快速接入Coze智能体-开箱即用-第2节
  • langgraph框架之初识
  • AI 编程工具—如何在 Cursor 中集成使用 MCP工具
  • Banana Pi BPI-RV2 RISC-V 路由器开发板发售, 全球首款RISC-V路由器
  • STM32单片机入门学习——第41节: [12-1] Unix时间戳
  • SpringAI入门:对话机器人
  • docker 镜像
  • MySQL中高级语法
  • 解决 Spring Boot 多数据源环境下事务管理器冲突问题(非Neo4j请求标记了 @Transactional 尝试启动Neo4j的事务管理器)
  • polkitd服务无法启动导致docker无法启动问题解决
  • 海关总署广东:广东外贸一季度进出口2.14万亿元 同期增长4.2%
  • 2.2/Q2,Charls最新文章解读
  • 【Python爬虫基础篇】--2.模块解析
  • 07_Docker 资源限制
  • Jenkins的使用及Pipeline语法讲解
  • Android Studio 常见报错
  • 从零开始学Python游戏编程31-类3
  • 深入简出:KL散度、交叉熵、熵、信息量简介、交叉熵损失
  • ubuntu学习day3
  • QT 文件和文件夹操作
  • 不降息就走人?特朗普试图开先例罢免美联储主席,有无胜算
  • 同比增长4.2%!一季度全国财政支出持续增长
  • 礼来公布口服降糖药积极结果,或年底前提交用于体重管理上市申请
  • 为护航企业“出海”,“无问西东·中外商会”海上沙龙举行
  • 昆明盘龙区一火灾调查报告公布:老人火盆取暖引燃房屋致身亡
  • 奥利弗·沙赫特博士:集群是产业集聚地,更是“超级连接器”