news 2026/3/26 23:02:03

Java对象创建的过程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java对象创建的过程

一、类加载过程

  1. 类加载检查
    • 当Java虚拟机(JVM)遇到new关键字时,它会先检查要创建的对象类是否已经被加载、链接和初始化。如果尚未加载,JVM会通过类加载器(ClassLoader)加载对应类的.class文件。
  2. 类加载
    • 类加载包括三个子步骤:加载、连接、初始化。
      • 加载:通过权限定类名,读取 class 文件内容为二进制流;二进制流转换成方法区(永久代或元数据区)的运行时 C++类字节码对象 Klass;最后再在堆上生成一个 Class 对象,用来间接获取元数据区的类定义信息,静态对象也保存在 Class 对象中。
      • 连接
        • **验证:**验证文件格式、字节码元数据、语法、符号引用;
        • 准备:为类的静态变量分配内存,并赋予默认初始值(例如0或null),但不会执行任何实际的初始化代码。
        • **解析:**将符号引用替换为直接引用。
      • 初始化:类的静态变量赋予正确的初始值,执行静态块;可能涉及父类初始化。
  3. 内存分配
    • JVM为新创建的对象分配内存空间。对象内存主要包括对象头、实例数据以及可能的对齐填充。
      • 对象头:存储对象自身的元数据如哈希码、锁状态标志、GC分代年龄等信息,以及指向其类元数据(Class对象)的指针。
      • 实例数据:存储类中定义的字段的实际数据。
      • 对齐填充:非必须,为了满足JVM对内存地址对其的要求而填充的额外空间。
  4. 初始化零值
    • 分配内存后,JVM会对对象的所有字段(包括实例变量)分配默认的初始值。
  5. 显式初始化:接下来,如果有在字段声明时直接赋予的初始值(例如int a = 10;),这些值会在构造函数执行前被赋予相应的变量。
  6. **对象头 必要信息设置 **主要是对象头中类的源数据信息,哈希码 ,对象 GC 分代年龄等。
  7. 初始化
    • 构造器初始化:调用类的构造方法(即构造器)进行初始化,执行构造器中的初始化代码,此时才会给实例变量赋予程序员指定的初始值。
    • 如果类中有父类并且还没有被初始化,则先初始化父类。
  8. 对象构造完成
    • 构造方法执行完毕后,对象就完全构造出来了,可以被程序正常使用。

二、对象内存分配方式

内存分配方式根据不同的收集器策略可分为两种,不同的收集器的堆内存规整程度不一致所以有两种分配策略。

指针碰撞 Bump The Pointer

在使用指针碰撞策略时,Java堆被假设为一个连续的内存空间,被分为已用和未用两部分,中间由一个指针作为分界线。当新对象需要内存时,JVM只需将指针向未用空间一侧移动与对象大小相等的距离即可。这种方式适用于使用标记-清除复制算法的垃圾收集器,因为这些算法能够整理出连续的内存空间。

空闲列表 Free List

如果Java堆中的内存不是连续的,或者已被使用的内存和未被使用的内存相互交错(这种情况通常发生在使用标记-整理或分代收集算法的垃圾收集器中),那么空闲列表策略更为适用。在这种情况下,JVM维护一个列表来记录堆中各个小块的可用内存空间。当新对象需要内存时,JVM会从列表中找到一个足够大的空闲块分配给对象,并更新列表。这种方法不需要连续的内存空间,但管理成本相对较高。

三、内存分配的安全问题

堆是线程间共享的一块儿区域,所以多个线程同时创建对象时都涉及对内存空间的申请和分配,那么内存分配就可能出现线程安全问题。依赖以下机制解决多线程安全问题,一种是** CAS 乐观锁机制**,一种是TLAB 本地线程分配缓冲机制

Thread Local Allocation Buffer (TLAB) 本地线程分配缓冲:每个线程有一个属于自己的预分配内存空间,JVM 首先通过 CAS 为线程申请一块儿预分配内存。这样当某个线程需要申请新的内存空间时首先现在自己的 TLAB 上分配,能减少内存分配冲突。后续 TLAB 内存不足了才会 CAS 申请一块儿新的 LATB 或者直接在 Eden 区直接分配。

Compare-and-Swap (CAS)在某些JVM实现中,可能会使用CAS操作来实现无锁的线程安全内存分配。CAS是一种硬件级别的原子操作,允许线程在不加锁的情况下比较并交换内存中的值,从而减少锁带来的性能开销,并能有效防止数据竞争。

四、对象如何进入老年代

  1. 新生代:刚创建的对象默认进入新生代的 Eden 区
  2. 进入老年代的条件:四种情况
    1. 熬过了多次 minorGC ,每次 MinorGC 过后对象的年龄就会+1,存活超过 15 次之后就会进入老年代。该次数可通过参数控制-XX:MaxTenuringThreshold
    2. 动态年龄判断机制:MinorGC 后,如果 Survivor 区中的一批对象大雨了这块 Survivor 区的 50%就会将大于等于这批对象年龄最大值的所有对象直接进入老年代。
      1. 举例 S1 中有 年龄为 1 、2、3、4 的一批对象,其中 234 年龄的加起来超过 S1 的 50%,那么年龄大于等于 4 的对象就直接进入老年代了。
    3. Serial 和 ParNew 收集器,大对象直接进入老年代。例如大字符串和数组 可通过-XX:PertenureSizeThreshold 配置 默认为 1M
    4. MinorGC 后,存活的对象太多无法放入 Sruvivor 区域,会触发空间分配担保机制。将存活的对象移入老年代

分配担保机制(空间担保) Allocation Assurance Mechanism

什么是分配担保机制

在 JVM 中,空间分配担保机制(Space Allocation Guarantee Mechanism)是一种确保在进行垃圾收集时,有足够的空间来处理对象晋升分配的策略。这种机制主要用于新生代垃圾收集(Minor GC)和老年代垃圾收集(Major GC 或 Full GC)之间的协调,以避免出现内存不足的情况。

:::success
用老年代的空间,来担保新生代的垃圾回收可以成功执行并腾出空间。会将新生代存活的对象转移到老年代中。保证新分配内存能直接成功。

  1. young 分区内存不足以创建新对象

:::

内存担保的原理

MinorGC前

  1. 第一步:判断老年代可用内存是否小于新时代对象全部对象大小,如果小于则继续判断,大于则可进行MainorGC
  2. 第二步:老年代小于存活对象,则判断老年代内存是否小于每次MinorGC后进入老年代的平均大小
    1. 小于平均大小,则进行FullGC,再判断是否能保存得下存活对象,放不下则OOM
    2. 大于平均大小,则进行MinorGC

MinorGC后

  1. 如果存活对象小于Survivor区,则直接进入Survivor区
  2. 如果存活对象大于Survivor区,但是小于老年代可用内存,则直接进入老年代
  3. 如果存活对象大于Survivor区,还大于老年代,则尝试进行一次FullGC,FullGC后再次判断,如果放不下存活对象则会OOM

分配担保的配置

  1. **-XX:HandlePromotionFailure:**这个参数控制是否允许晋升失败。如果设置为 true,JVM 会在 Minor GC 时尝试晋升对象,即使老年代空间不足,也会尝试进行一次 Minor GC。如果失败,则触发 Full GC。这个参数在 Java 6 之后已经被默认取消使用。
  2. -XX:PretenureSizeThreshold:这个参数指定大对象直接在老年代分配的大小阈值。超过该阈值的对象直接分配到老年代,避免在新生代频繁复制。
  3. **-XX:MaxTenuringThreshold:**这个参数控制对象在新生代中经历多少次 GC 后晋升到老年代。较高的阈值可以减少对象晋升,但会增加新生代的 GC 频率。
  4. -XX:TargetSurvivorRatio:这个参数控制每次 Minor GC 后目标存活区(Survivor Space)的利用率。JVM 会根据这个参数调整对象晋升的阈值。

五、验证

大对象直接进入老年代

/** * 测试:大对象直接进入到老年代 * -Xmx60m -Xms60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails * -XX:PretenureSizeThreshold * */publicclassYoungOldArea{publicstaticvoidmain(String[]args){byte[]buffer=newbyte[1024*1024*20];//20M}}-XX:NewRatio=2新生代与老年代比值-XX:SurvivorRatio=8新生代中,Eden与两个Survivor区域比值-XX:+PrintGCDetails打印详细GC日志-XX:PretenureSizeThreshold对象超过多大直接在老年代分配,默认值为0,不限制

对象内存分代晋升演示

/* -Xmx600m -Xms600m -XX:+PrintGCDetails */publicclassHeapInstance{publicstaticvoidmain(String[]args){List<Picture>list=newArrayList<>();while(true){try{Thread.sleep(20);}catch(InterruptedExceptione){e.printStackTrace();}list.add(newPicture(newRandom().nextInt(1024*1024)));}}}classPicture{privatebyte[]pixels;publicPicture(intlength){this.pixels=newbyte[length];}}

通过可视化插件可以看到

  1. Eden区满了之后,就会进行MinorGC,MinorGC时会将Survior放不下的对象存到old老年代
  2. 老年代也满了之后,发生了三次MinorGC,未释放出可用空间后,进行了三次FullGc最后抛出了OOM
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/26 0:08:16

AWS推出AI图像编辑新突破:用说话就能精准移动图片中的物体!

这项来自香港中文大学、AWS智能AI部门、亚马逊云服务和亚马逊机器人团队的联合研究发表于2025年1月&#xff0c;论文编号为arXiv:2601.02356v1。研究团队由谭靖、张兆阳、沈彦涛、蔡嘉瑞等多位学者组成&#xff0c;有兴趣深入了解的读者可以通过该编号查询完整论文。想要修改照…

作者头像 李华
网站建设 2026/3/26 12:02:18

从案例到技巧:Agentic AI提示设计的实战总结(提示工程架构师版)

好的&#xff0c;技术架构师&#xff01;基于您提供的主题&#xff0c;我为您精心构思一篇面向**具备基础提示工程知识、致力于构建复杂可靠Agent系统的高级用户&#xff08;如提示工程架构师、技术负责人、高级AI工程师&#xff09;**的实战深度总结文章。文章将聚焦可落地的设…

作者头像 李华
网站建设 2026/3/26 4:08:07

光谱共焦技术在高精度尺寸与3D表面缺陷检测中的工业应用研究

摘要&#xff1a;随着智能制造与精密工业的快速发展&#xff0c;对非接触、高精度、高速度的在线检测技术需求日益迫切。以海伯森技术推出的系列高端光学传感器深入剖析其基于光谱共焦位移测量与光谱共焦成像的核心原理。重点阐述该技术如何在微观尺度上实现纳米级精度的三维尺…

作者头像 李华
网站建设 2026/3/26 2:51:06

GDAL 实现矢量裁剪

前言 ❝ 矢量数据作为数据处理的半壁江山&#xff0c;在日常工作中涉及到多种操作&#xff0c;矢量数据裁剪尤其具有代表性和重要性&#xff0c;是常用操作&#xff0c;核心原理为从指定数据中提取出目标范围。在之前的文章中讲了如何使用GDAL或者ogr2ogr工具将txt以及csv文本数…

作者头像 李华
网站建设 2026/3/25 20:15:13

华为研究团队突破代码修复瓶颈,8B模型击败32B巨型对手!

这项由华为技术有限公司、南洋理工大学、香港大学和香港中文大学联合完成的突破性研究发表于2026年1月&#xff0c;论文编号为arXiv:2601.01426v1。研究团队通过一种名为SWE-Lego的创新训练方法&#xff0c;让相对较小的8B参数模型在软件代码自动修复任务上的表现超越了许多32B…

作者头像 李华
网站建设 2026/3/26 8:24:45

git创建远程分支、分支合并、删除分支

# git创建远程分支 #查看分支 git branch -a#创建git分支 git checkout -b branch_name#push到远程仓库 git push -u origin branch_name# git 合并分支 # 标准合并&#xff08;保留分支历史&#xff09; git checkout master # 先切到要「接受」变更的分支 git pull …

作者头像 李华