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

Java对象的GC回收年龄的研究

目录

1、介绍

2、内存结构

2.1、普通对象

2.2、数组对象

2.3、数组长度作用

2.4、为什么 age 用 4 位?

3、对象头组成

3.1、Mark Word(标记字段)

3.2、Class Pointer(类指针)

4、GC 发生的位置

4.1、新生代

4.2、老年代

4.3、元空间(Metaspace,Java 8+)

5、对象晋升

5.1、15 次 Minor GC

5.2、动态年龄判定

5.3、Survivor区存活时间

5.4、修改晋升年龄阈值

6、更长的存活的方案

6.1、扩大 Survivor 区大小

6.2、G1 垃圾回收器


前言

        在 Java 中,GC 年龄(GC Age 或 Tenuring Threshold) 是用于衡量对象在 年轻代(Young Generation) 中经历垃圾回收(GC)次数的指标。GC 年龄决定了对象何时从年轻代晋升到老年代(Old Generation),是 Java 垃圾回收机制中重要的优化策略之一。

不同垃圾回收器的处理方式如下:

1、Serial/Parallel Scavenge

            使用经典的 复制算法,对象在 Survivor 区之间来回复制,GC 年龄逐步增加。晋升到老年代的逻辑与上述规则一致。

    2、G1(Garbage-First)

    1.分区机制

            年轻代划分为多个 Region,对象晋升到老年代的 Region。

    2.年龄表(Age Table)

            G1 维护每个 Region 的年龄表,记录对象的存活情况和 GC 年龄分布。

    3.动态调整晋升阈值

            G1 会根据 Region 的填充情况动态调整晋升策略,优化吞吐量和延迟。

    3、CMS

            与 Serial/Parallel 类似,但 CMS 更关注减少 Full GC 的停顿时间,晋升策略会更保守。


    1、介绍

    关于java对象,存放在jvm的堆内存里面,在jdk1.8版本后,如下图所示:

            通过合理配置和监控 GC 年龄,可以显著提升 Java 应用的性能和稳定性。


    2、内存结构

    如下图所示:

    2.1、普通对象

    • 对象头
      • Mark Word(64 位)
      • Class Pointer(64 位,或 32 位,若启用指针压缩)
    • 实例数据
      • 存储对象的字段值(如 intString 等)
    • 对齐填充
      • 补齐到 8 字节边界

    2.2、数组对象

    • 对象头
      • Mark Word(64 位)
      • Class Pointer(64 位,或 32 位,若启用指针压缩)
      • Array Length(32 位,固定 4 字节)
    • 实例数据
      • 存储数组元素(如 int[10] 会存储 10 个 int
    • 对齐填充
      • 补齐到 8 字节边界

    区别:

    2.3、数组长度作用

    1.数组的元数据

            数组需要知道自己的长度(元素个数),而普通对象的字段数量和类型由类定义决定,不需要在对象头中存储。

    2.内存管理

            数组的元素是连续存储的,JVM 需要通过长度字段快速访问元素(如 array[index])。

    2.4、为什么 age 用 4 位?

    如下图所示:

    1. 4 位二进制数的限制

    • age 字段在 Mark Word 中仅占 4 位,因此最大可表示的值为2^4−1=15。
    • 当对象在 Survivor 区中经历 15 次 Minor GC 后,其 age 达到 15,会被晋升到老年代。

    2. 设计权衡

    • 节省空间:Mark Word 的总大小是固定的(64 位或 32 位),每个字段的位数需要精打细算。age 只用 4 位,避免浪费。
    • 满足需求:大多数应用场景中,对象的存活周期不会超过 15 次 Minor GC。如果超过,直接晋升到老年代即可。
    • 兼容性:若 age 需要更多位(如 5 位),则需重新设计 Mark Word 的布局,可能影响其他字段(如哈希码、锁状态)的存储。

    3、对象头组成

     关于mark word的数据结构可参考:对于Synchronized和Volatile的深入理解-CSDN博客

    Mark Word 的结构如下所示(以 64 位 JVM 为例):

    锁标志位可分为:

    • 00:轻量级锁

    • 01:无锁/偏向锁

    • 10:重量级锁

    • 11:GC标记

    对象头(Object Header)是对象在内存中的元数据区域,通常由两部分组成:

    1. Mark Word:存储对象的运行时数据,如哈希码、锁状态、GC 分代年龄(age)等。
    2. Class Pointer:指向对象所属类的元数据(如 java.lang.Class)。

    3.1、Mark Word(标记字段)

     关于mark word可以参考:对于Synchronized和Volatile的深入理解-CSDN博客

    • 作用:存储对象的运行时数据,例如:
      • 哈希码(Hash Code)
      • 锁状态(无锁、偏向锁、轻量级锁、重量级锁)
      • GC 分代年龄(age)
      • 偏向线程 ID
      • 同步锁信息
    • 大小
      • 32 位 JVM:32 位
      • 64 位 JVM:64 位(可启用压缩指针优化为 32 位)

    3.2、Class Pointer(类指针)

    • 作用:指向对象所属类的元数据(即 java.lang.Class 对象),用于访问类的静态信息(如方法、字段等)。
    • 大小
      • 32 位 JVM:32 位
      • 64 位 JVM:64 位(可启用压缩指针优化为 32 位)

    4、GC 发生的位置

    在 JVM 中,内存被划分为不同的区域,不同区域的 GC 行为和触发条件不同:

    如下图所示:

    4.1、新生代

    • 组成:Eden 区 + 两个 Survivor 区(From 和 To)。
    • GC 类型Minor GC(年轻代 GC)
    • 触发条件
      • 当 Eden 区分配对象时,空间不足。
      • 新生代中的对象存活时间较短,Minor GC 频繁发生。

    4.2、老年代

    • 组成:存放长期存活的对象。
    • GC 类型Major GC / Full GC
    • 触发条件
      • 老年代空间不足。
      • Minor GC 后对象无法放入 Survivor 区,需要晋升到老年代,但老年代空间不足。
      • 元空间(Metaspace)不足(在 Java 8+ 中)。
      • 显式调用 System.gc()

    4.3、元空间(Metaspace,Java 8+)

    • 作用:存储类的元数据(如类定义、方法信息等)。
    • GC 类型:Full GC(当元空间不足时)。

    5、对象晋升

    而对于<jdk1.8:还有持久代。对于>=jdk1.8: metaspace。

    如下图所示:堆结构在分配内存空间的时候:老年代占比2/3,新生代1/3。

    而新生代里面eden和from、to区的内存占比分别为8:1:1。

            关于 GC(垃圾回收)发生的位置 以及 “15次”的含义,可以从 JVM 的内存结构和 GC 策略两个角度来解释。

    5.1、15 次 Minor GC

              Minor GC 仅发生在 年轻代(Young Generation),即 Eden 区 + Survivor 区(From + To)


            15次 Minor GC 是对象在 Survivor 区 中存活的次数,不包括老年代的 Full GC

    1. 对象晋升规则

    • 对象在 Eden 区分配后,经历 Minor GC:
      • 如果存活,则被复制到 Survivor 区。
      • 每次 Minor GC 后,对象的 age 加 1。
      • 当 age 达到 MaxTenuringThreshold(默认 15)时,对象晋升到老年代。

            Survivor1 和 Survivor2 区域之间的对象迁移是通过“标记-复制”算法实现的

    2. 4 位二进制数的硬性限制

    • age 最大只能是 15,因此 15 次 Minor GC 是 JVM 的硬性设计限制。
    • 如果需要更大的 age,必须扩展 Mark Word 的位数,但会牺牲其他字段的空间。

    5.2、动态年龄判定

    触发条件

            如果某个年龄段的对象总大小超过 Survivor 区的 50%,即使未达到最大年龄阈值,也会被直接晋升到老年代。

    例如:如果年龄为 3 的对象总大小超过 Survivor 区的 50%,则所有年龄 >=3 的对象都会晋升。

    5.3、Survivor区存活时间

    • 存活时间:对象在 Survivor 区的存活时间取决于其 年龄增长速度 和 晋升条件

    1.最短时间

            如果对象在第一次 Minor GC 后就晋升到老年代(如动态年龄判定触发),则仅存活 1 次 Minor GC。

    2.最长时间

            如果对象一直存活到年龄达到 MaxTenuringThreshold,则最多存活 MaxTenuringThreshold 次 Minor GC。

            默认情况下,最多存活 15 次 Minor GC。

    5.4、修改晋升年龄阈值

    可以通过 JVM 参数调整对象晋升的年龄阈值:

    -XX:MaxTenuringThreshold=15
    
    • 如果尝试设置 MaxTenuringThreshold=20,JVM 会将其 自动调整为 15,并可能输出警告或忽略该设置。
    • 示例 JVM 输出:
    [Warning] MaxTenuringThreshold is set to 20, but it will be adjusted to 15 due to 4-bit limit.
    

    6、更长的存活的方案

            JVM 会根据 Survivor 区的使用情况动态调整晋升阈值

    如果 Survivor 区使用率低,JVM 会适当提高阈值,让对象多经历几次 Minor GC 再晋升。

    如果 Survivor 区使用率高,JVM 会降低阈值,让对象提前晋升到老年代。

    可以通过以下方式间接实现:

    6.1、扩大 Survivor 区大小

    • 参数-XX:SurvivorRatio=N
      • 默认值为 8,表示 Eden 区与 Survivor 区的比例为 8:1:1。
      • 例如,-XX:SurvivorRatio=4 会分配更大的 Survivor 区,容纳更多存活对象,从而减少晋升频率。

    6.2、G1 垃圾回收器

            G1(Garbage-First) 不依赖固定大小的 Survivor 区,而是通过分区(Region)管理内存,对对象晋升的控制更灵活。

              可以通过 -XX:G1MixedGCCountTarget 等参数优化晋升策略。

              G1通过统计每个Region的垃圾密度(Garbage Fraction),预测回收收益。存活时间长的对象所在的Region若垃圾密度低,回收优先级较低,从而减少不必要的回收操作。

              G1在Full GC前会优先回收老年代中垃圾比例高的Region(Garbage-First策略)。


      总结:


      总结

      通过这种设计,JVM 在对象头空间有限的情况下,平衡了性能、内存效率和功能需求。


      参考文章:

      1、对于Synchronized和Volatile的深入理解-CSDN博客

      2、关于对JVM的知识整理_谈谈你对jvm的理解-CSDN博客

      相关文章:

    • 反病毒反垃圾U-Mail邮件系统从容应对
    • python制造一个报错
    • websocket简介与基本使用
    • 15:00开始面试,15:06就出来了,问的问题有点变态。。。
    • 《MySQL:MySQL事务特性》
    • uniapp中vue3和pinia安装依赖npm install失败
    • Web安全科普:构建数字世界的“防盗门”
    • Xinference 命令大全:从模型部署到管理
    • coze从入门到入土:excel表格批量导入数据库工作流制作【解决节点使用上限】 + API接口上传文件和用户需求
    • .NET 通过命令行解密web.config配置
    • PostgreSQL MCP 使用案例
    • Ascend的aclgraph(九)AclConcreteGraph:e2e执行aclgraph
    • Digi XBee XR 系列介绍
    • 第四章 部件篇之下拉列表部件
    • 用MCP往ppt文件里插入系统架构图
    • [QMT量化交易小白入门]-五十三、总收益率187%,年化收益率在5.57%,二十年回测,每月调仓,获取稳定的收益
    • 用C语言实现了——一个基于顺序表的插入排序演示系统
    • 班会内容模板
    • GitHub 趋势日报 (2025年05月14日)
    • 沃伦森智能无功补偿系统解决电力电容器频繁投切的隐患
    • 坚持吃素,是不是就不会得高血脂了?
    • 联合国第二届运动会闭幕,刘国梁受邀成为“联合国运动会大使”
    • AI观察|从万元到百万元,DeepSeek一体机江湖混战
    • 中方发布会:中美经贸高层会谈氛围是坦诚的、深入的、具有建设性的
    • 贵州省总工会党组成员、副主席梁伟接受审查调查
    • 巴基斯坦对印度发起网络攻击,致其约70%电网瘫痪