简述一下 Java 中创建一个对象的过程

本文深入探讨了Java中对象的生命周期,从类加载到内存分配,再到对象初始化,详细阐述了对象创建的每一步。讨论了指针碰撞与空闲列表两种内存分配策略,以及线程安全的解决办法,如使用TLAB。最后,介绍了对象初始化后的状态,即执行init方法以确保对象完全可用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类的生命周期

解析:回答这个问题首先就要清楚类的生命周期

下图展示的是类的生命周期流向:   
在这里插入图片描述

Java中对象的创建就是在堆上分配内存空间的过程,此处说的对象创建仅限于new关键字创建的普通Java对象,不包括数组对象的创建。

对象创建过程

  1. 检测类是否被加载:
      当虚拟机执行到new时,会先去常量池中查找这个类的符号引用。如果能找到符号引用,说明此类已经被加载到方法区(方法区存储虚拟机已经加载的类的信息),可以继续执行;如果找不到符号引用,就会使用类加载器执行类的加载过程,类加载完成后继续执行。

  2. 为对象分配内存:
      类加载完成以后,虚拟机就开始为对象分配内存,此时所需内存的大小就已经确定了。只需要在堆上分配所需要的内存即可。

具体的分配内存有两种情况:第一种情况是内存空间绝对规整,第二种情况是内存空间是不连续的。

对于内存绝对规整的情况相对简单一些,虚拟机只需要在被占用的内存和可用空间之间移动指针即可,这种方式被称为指针碰撞。
对于内存不规整的情况稍微复杂一点,这时候虚拟机需要维护一个列表,来记录哪些内存是可用的。分配内存的时候需要找到一个可用的		内存空间,然后在列表上记录下已被分配,这种方式成为空闲列表。

分配内存的时候也需要考虑线程安全问题,有两种解决方案:
第一种是采用同步的办法,使用CAS来保证操作的原子性。
另一种是每个线程分配内存都在自己的空间内进行,即是每个线程都在堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),分配内存的时候再TLAB上分配,互不干扰。

  1. 为分配的内存空间初始化零值:
      对象的内存分配完成后,还需要将对象的内存空间都初始化为零值,这样能保证对象即使没有赋初值,也可以直接使用

  2. 对对象进行其他设置:
      分配完内存空间,初始化零值之后,虚拟机还需要对对象进行其他必要的设置,设置的地方都在对象头中,包括这个对象所属的类,类的元数据信息,对象的hashcode,GC分代年龄等信息。

  3. 执行 init 方法:
      执行完上面的步骤之后,在虚拟机里这个对象就算创建成功了,但是对于Java程序来说还需要执行init方法才算真正的创建完成,因为这个时候对象只是被初始化零值了,还没有真正的去根据程序中的代码分配初始值,调用了init方法之后,这个对象才真正能使用。

到此为止一个对象就产生了,这就是new关键字创建对象的过程。过程如下

在这里插入图片描述

### Java对象创建过程、内存分配与初始化步骤 Java对象创建过程一个复杂且精细的过程,涉及加载检查、内存分配以及对象初始化等多个阶段。以下是关于Java对象创建过程中涉及的内存分配和初始化等细节的详细说明。 #### 1. 加载检查 在对象创建之前,JVM会先检查该是否已经被加载、解析和初始化。如果尚未完成这些操作,则需要先执行加载流程[^1]。加载是通过加载器(ClassLoader)完成的,主要包括加载、链接(验证、准备、解析)和初始化三个阶段。 #### 2. 分配内存 当加载检查通过后,JVM将为新生对象分配内存。内存分配的主要方式有两种: - **指针碰撞**:如果Java堆中的内存是规整的,即所有用过的内存都放在一边,空闲的内存放在另一边,中间维护一个指针作为分界点的指示器,那么分配内存就仅仅是把指针向空闲空间方向移动一段与对象大小相等的距离[^2]。 - **空闲列表**:如果Java堆中的内存不是规整的,已使用的内存和空闲内存相互交错,则需要维护一个列表记录哪些内存块是可用的,在分配时从列表中找到一块足够大的空间供对象使用。 内存分配的具体方式由Java堆是否规整决定,而Java堆是否规整又取决于垃圾收集器是否带有压缩整理功能。 #### 3. 初始化零值 内存分配完成后,JVM会对对象进行必要的设置,例如确定对象头信息(包括哈希码、GC分代年龄等)。此外,JVM还会对对象实例变量进行零值初始化(如整型变量初始化为0,引用型初始化为null),这一过程发生在字段显式初始化和构造函数调用之前[^3]。 #### 4. 执行初始化方法 在内存分配和零值初始化完成后,JVM会调用`<init>`方法来执行对象的初始化代码。`<init>`方法是由编译器根据中的字段赋值语句和构造函数内容自动生成的,用于完成对象的显式初始化。例如,在以下代码中,`i = 13`会被编译器加入到`<init>`方法中[^4]: ```java class Obj { int i = 13; } ``` #### 5. 返回对象引用 当`<init>`方法执行完毕后,一个真正可用的对象就产生了。此时,JVM会返回对象的引用,程序可以通过该引用来访问对象的属性和方法[^5]。 #### 注意事项 需要注意的是,`new`操作非原子性操作。在多线程环境下,可能会出现指令重排的问题,导致部分线程看到未完全初始化的对象。为了避免这种情况,可以使用`volatile`关键字修饰对象引用,禁止指令重排。 --- ### 示例代码 以下代码展示了Java对象创建过程包括内存分配和初始化: ```java public class Test { public static void main(String[] args) { Obj object_1 = new Obj(); // 对象创建过程 System.out.println(object_1.i); // 输出初始化后的字段值 } } class Obj { int i = 13; // 字段初始化 } ``` 在上述代码中: 1. JVM首先检查`Obj`是否已被加载。 2. 然后为`object_1`分配内存。 3. 接着对`object_1`进行零值初始化。 4. 最后执行`<init>`方法,将`i`初始化为13。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值