跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Java 中 new 创建对象的完整流程解析

Java 中 new 对象涉及类加载、内存分配、初始化及引用返回。首先检查类是否加载,未加载则执行加载验证准备解析初始化流程。随后在堆内存分配空间,根据 GC 策略采用指针碰撞或空闲列表。接着零值填充实例字段,设置对象头包含 Mark Word 和类型指针。最后执行构造方法 <init> 完成业务初始化并返回引用。逃逸分析可能优化为栈上分配。核心在于 JVM 先完成内存创建再通过构造方法初始化。

BigDataPan发布于 2026/2/28更新于 2026/6/1130 浏览

Java 中 new 一个对象 的过程是从字节码解析到内存分配、初始化、引用返回的完整链路,涉及 JVM 类加载、内存管理、构造方法执行等核心机制。以下按「JVM 层面的核心步骤」+「代码层面的直观拆解」展开,兼顾底层原理和实际理解:

一、核心前提:类必须先加载(首次创建对象时)

如果该类从未被 JVM 加载过,执行 new 前会先触发类加载流程(加载→验证→准备→解析→初始化),确保类的元数据(如字段、方法、常量池)被加载到方法区(JDK 1.8+ 为元空间)。

  • 加载:通过类的全限定名读取字节码文件(.class);
  • 初始化:执行静态代码块(<clinit>)、静态变量赋值(如 static int a = 10);
  • 注意:类加载仅执行一次,后续创建该类对象时跳过此步骤。

二、new 对象的核心步骤(JVM 层面)

以 User user = new User("张三", 20); 为例,完整流程如下:

步骤 1:检查类加载状态 + 分配内存

JVM 先确认 User 类已加载,然后为新对象分配堆内存:

  • 内存大小确定:根据类的元数据(字段类型、数量)计算对象所需内存(如对象头 + 实例字段 + 对齐填充);
  • 内存分配方式:
    • 「指针碰撞」:堆内存规整(Serial/ParNew 收集器),JVM 移动空闲指针,划分出对象所需内存;
    • 「空闲列表」:堆内存碎片化(CMS 收集器),JVM 从空闲列表中找到足够大的内存块分配;
  • 线程安全保障:
    • 方案 1:CAS + 失败重试(保证分配原子性);
    • 方案 2:TLAB(本地线程分配缓冲)—— 每个线程在堆中预留一小块内存,优先在 TLAB 分配,避免竞争(默认开启)。

步骤 2:内存初始化(零值填充)

分配完内存后,JVM 会将对象的实例字段初始化为对应类型的零值(不执行赋值语句,仅清空内存):

  • 例如:User 的 name 字段(String 类型)被设为 null,age 字段(int 类型)被设为 0,引用类型默认 null,基本类型默认对应零值(boolean→false,long→0L 等);
  • 目的:保证对象字段在构造方法执行前,不会出现'未初始化的脏数据'。

步骤 3:设置对象头(Object Header)

在分配的内存中设置对象头信息,包含:

  • Mark Word:存储对象的哈希值、GC 分代年龄、锁状态、偏向锁线程 ID 等;
  • 类型指针:指向对象所属类的元数据(如 User.class),JVM 通过该指针确认对象的类型;
  • (数组对象额外)数组长度:若为数组对象,还会存储数组长度字段。

步骤 4:执行实例初始化方法(<init>)

这是「代码层面感知最明显」的步骤,JVM 调用对象的构造方法(<init> 是编译器生成的初始化方法,对应代码中的构造函数):

  • 执行顺序:
    1. 先调用父类的 <init> 方法(隐式 super(),若未显式调用,编译器自动添加),递归直到 Object 类;
    2. 执行实例变量的显式赋值(如 private String name = "默认名");
  • 执行构造方法中的自定义逻辑(如 this.name = name; this.age = age;);
  • 关键:<init> 方法是对象初始化的核心,只有执行完 <init>,对象才是'完整可用'的。
  • 步骤 5:返回对象引用

    JVM 将堆中对象的内存地址赋值给栈中的引用变量(如 user):

    • 注意:引用变量(user)存储在栈帧的局部变量表中,指向堆中的实际对象;
    • 特殊情况:JIT 优化可能将对象'标量替换'到栈上(逃逸分析),避免堆内存分配(如局部对象未逃逸出方法)。

    三、代码层面的直观拆解(结合示例)

    以自定义 User 类为例,直观对应上述步骤:

    class User {
        // 实例字段
        private String name;
        private int age;
    
        // 静态代码块(类加载时执行,仅一次)
        static {
            System.out.println("User 类初始化(静态代码块)");
        }
    
        // 构造方法
        public User(String name, int age) {
            this.name = name;
            this.age = age;
            System.out.println("构造方法执行:初始化 name 和 age");
        }
    }
    
    // 创建对象
    public class Test {
        public static void main(String[] args) {
            User user = new User("张三", 20);
        }
    }
    
    执行输出(首次创建):
    User 类初始化(静态代码块) 构造方法执行:初始化 name 和 age 
    
    对应步骤:
    1. 首次执行 new User() → 触发 User 类加载,执行静态代码块;
    2. JVM 为 User 对象分配堆内存;
    3. 内存零值填充:name=null,age=0;
    4. 设置对象头(Mark Word + 指向 User.class 的类型指针);
    5. 执行 <init> 方法:
      • 调用 Object 的 <init>(隐式);
      • 执行构造方法逻辑,将 name 设为'张三',age 设为 20;
    6. 将堆中对象地址赋值给栈中的 user 引用。

    四、关键补充:易混淆的细节

    1. new vs clone vs 反射创建对象

    • new:触发类加载 + 完整的 <init> 执行;
    • clone:不执行构造方法,直接拷贝已有对象的内存(浅拷贝);
    • 反射(Class.newInstance()/Constructor.newInstance()):触发 <init>,但可绕过访问权限(如私有构造)。

    2. 逃逸分析对 new 的影响

    若对象未逃逸出方法(如仅在方法内使用),JIT 会优化为「栈上分配」,无需在堆中创建,减少 GC 压力:

    public static void test() {
        // 对象仅在方法内使用,逃逸分析后栈上分配
        User u = new User("李四", 25);
    }
    

    3. 构造方法的本质

    构造方法不是'创建对象',而是'初始化对象'—— 对象的内存分配在构造方法执行前已完成,构造方法仅负责给字段赋值。

    五、总结:new 对象的核心链路

    类加载(首次) → 分配堆内存 → 零值填充 → 设置对象头 → 执行<init>(父类构造 + 实例赋值 + 自定义逻辑) → 返回对象引用 
    
    • 核心:JVM 先完成'内存层面的对象创建',再通过构造方法完成'业务层面的初始化';
    • 关键:只有执行完 <init>,对象才是合法可用的,否则可能出现字段未初始化的异常。

    目录

    1. 一、核心前提:类必须先加载(首次创建对象时)
    2. 二、new 对象的核心步骤(JVM 层面)
    3. 步骤 1:检查类加载状态 + 分配内存
    4. 步骤 2:内存初始化(零值填充)
    5. 步骤 3:设置对象头(Object Header)
    6. 步骤 4:执行实例初始化方法(<init>)
    7. 步骤 5:返回对象引用
    8. 三、代码层面的直观拆解(结合示例)
    9. 执行输出(首次创建):
    10. 对应步骤:
    11. 四、关键补充:易混淆的细节
    12. 1. new vs clone vs 反射创建对象
    13. 2. 逃逸分析对 new 的影响
    14. 3. 构造方法的本质
    15. 五、总结:new 对象的核心链路
    • 💰 8折买阿里云服务器限时8折了解详情
    • Magick API 一键接入全球大模型注册送1000万token查看
    • 🤖 一键搭建Deepseek满血版了解详情
    • 一键打造专属AI 智能体了解详情
    极客日志微信公众号二维码

    微信扫一扫,关注极客日志

    微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

    更多推荐文章

    查看全部
    • Dart 设计模式:原型模式
    • NewStarCTF2025 Week2 Web 题目解析
    • 网络安全基础与黑客技术入门知识详解
    • 10 分钟搭建专属 AI Agent 全流程实操指南
    • 基于 Spring Cloud 的分布式智能推荐系统实现
    • 前端面试核心知识点与八股文整理
    • Vue3 中模板调用方法提示不存在的排查与修复
    • Python、NumPy、Pandas 与 Matplotlib 版本兼容指南
    • 自组织映射网络(SOM)原理与可视化实现
    • Windows 系统安装 Neo4j 图数据库指南
    • Java 方法封装与递归思想详解
    • 2025 年第十六届蓝桥杯省赛 JavaB 组真题详解
    • 香山 RISC-V 处理器 FPGA 原型构建实战指南与核心技术
    • Whisper 语音识别库启用 CUDA 加速配置指南
    • SVPWM 过调制算法原理及 Matlab 仿真
    • Java 设计模式详解
    • GitHub Codespaces 部署开源 AI 智能体 OpenClaw 指南
    • 低代码平台构建服装 ERP 系统实战:从设计到自动化流程
    • JavaScript 进阶必知的 3 个实用技巧
    • 通义万相 2.1 模型功能解析与部署指南

    相关免费在线工具

    • Keycode 信息

      查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

    • Escape 与 Native 编解码

      JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

    • JavaScript / HTML 格式化

      使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

    • JavaScript 压缩与混淆

      Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online