登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  java教程

Java G1从 humongous allocation 判断对象尺寸的实现方法

来源:17golang原创

时间:2026-09-20 00:52:26 383浏览 收藏

排查 Java G1 的大对象分配时,先记住一个可落地的判断:对象大小 S 至少达到一个 Region 大小 R 的一半,才会走 humongous 分配;如果它占用 n 个连续 Region,那么只能保守得到 (n-1)R 的区间。这个方法适合判断对象是否偏大、尾部空间浪费是否明显,但不能把堆级的 Humongous regions: X->Y 日志当成某一个对象的精确尺寸。

要点速览
  • humongous 阈值是当前 G1 Region 大小的一半,Region 大小由运行参数决定。
  • 连续 Region 数量能给出对象尺寸上下界,最后一个 Region 的剩余空间可能暂时不能复用。
  • Humongous regions 是堆级汇总;要精确知道对象来源,还需要对象级分配证据。

先确定 Region 大小和 humongous 阈值

G1 把 Java 堆切成等大的 Region,Region 是分配和回收的基本单位。对象大小达到半个 Region 后,会直接按老年代 humongous 对象处理。设当前 Region 大小为 R,那么判断下界就是 R/2,而不是固定的 1 MB 或 2 MB。

排查时先让日志打印堆和 Region 信息,固定堆大小也便于前后对照:

java -Xms4g -Xmx4g -XX:+UseG1GC \
  -Xlog:gc+heap=info:file=gc.log:time,uptime,level,tags \
  -jar app.jar
// -Xms 与 -Xmx 便于对照同一组堆参数;gc+heap=info 记录 Region 与 humongous 汇总。

如果必须显式设定 Region,可以使用 -XX:G1HeapRegionSize。它要求使用合法的 2 次幂范围;调大 Region 会改变阈值、连续空间和回收工作量,所以应把它当成容量与停顿之间的取舍,而不是看到大对象就直接修改。

Java G1 Region R、R/2 humongous 阈值与连续 Region 分配关系说明图
图1:G1 Region 与 humongous 阈值的静态说明图,不是运行截图。

用连续 Region 数量计算对象尺寸区间

humongous 对象从第一个 Region 的起始位置开始,向后占用连续 Region。若日志、转储或分析结果能确认一个对象占用了 n 个 Region,可以先按下面的范围估算:

static long lowerBoundBytes(long regionBytes, long regionCount) {
    // n 个 Region 至少意味着前 n-1 个 Region 被完整跨过。
    return Math.max(regionBytes / 2, (regionCount - 1) * regionBytes);
}

static long upperBoundBytes(long regionBytes, long regionCount) {
    // 对象不会超过它实际占用的 n 个 Region 总容量。
    return regionCount * regionBytes;
}

static long regionsFor(long regionBytes, long objectBytes) {
    // 向上取整,得到对象需要的连续 Region 数量。
    return (objectBytes + regionBytes - 1) / regionBytes;
}
// regionBytes 与 objectBytes 使用同一单位;生产代码还要检查溢出和非正数输入。

例如 R=4MB、对象占用 n=3 个 Region 时,尺寸大致落在 8MB 。这不是对象头、对齐和数组布局的精确测量,但足以帮助你判断“一个大数组是否已经跨到第三个 Region”以及尾部空间损失的量级。

G1 Region 数量 n、对象尺寸区间公式与 Humongous regions 堆级汇总边界关系图
图2:从 Region 数量推导尺寸区间的关系说明图,不是运行证据。

打开 gc+heap 日志并拆分聚合信息

gc+heap=info 输出中,Humongous regions: X->Y 表示一次回收前后堆中 humongous Region 的数量变化。它能回答“这类 Region 是否在增加、回收后是否下降”,但它把多个对象汇总在一起,不能回答“哪一个对象占了 3 个 Region”。

观察项能推出什么不能推出什么
Region 大小 Rhumongous 阈值约为 R/2不能推出对象实际大小
单个对象的 Region 数 n得到 (n-1)R 不能抹掉对象布局与对齐误差
Humongous regions: X->Y观察堆级数量和回收趋势不能单独归因到某个对象或类

因此,看到数量持续上升时,先保留 Region 大小、堆大小和时间点,再结合对象分配分析、堆转储或业务侧数组/缓存指标确认来源。不要只凭一行汇总日志修改 Region 参数。

按现象选择参数调整或精确证据

如果主要问题是 humongous Region 数量多、最后一个 Region 浪费明显或连续空间难以找到,可以评估增大 -XX:G1HeapRegionSize,同时观察停顿、记忆集和混合回收的变化。更大的 Region 可能减少跨 Region 引用,但单个 Region 搬迁和处理的活对象也可能更多。

如果问题是“到底哪个类分配了大对象”,就不要把日志汇总当作答案。应补充对象级证据,确认是大数组、序列化缓冲区还是缓存批次,并把采集窗口和业务流量对齐。参数调整解决的是分配布局,不会替代泄漏或批量尺寸治理。

  • 先确认 R,再计算 R/2,避免拿固定阈值套所有 JVM。
  • 能确认 n 才使用尺寸区间;只有 X->Y 时,只讨论趋势。
  • 调大 Region 前记录停顿和回收指标,保留可回滚配置。

常见问题

Humongous regions 数量能直接换算成对象大小吗?

不能。它是多个 humongous 对象占用 Region 的汇总,最多帮助判断总量和趋势。

对象刚超过半个 Region 会占几个 Region?

通常至少占一个 Region;对象越过一个 Region 边界后,需要向上取整到更多连续 Region,尾部剩余空间可能暂时不能给普通对象使用。

只调大 G1HeapRegionSize 就能解决 Full GC 吗?

不能保证。它可能缓解 humongous 碎片,但 Full GC 还受堆容量、标记是否及时完成、分配速率和回收压力影响。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>