You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用JFR后批量OOM问题咨询:成因、时长关联与开销估算

问题分析与解答

背景信息

生产环境启用JFR配置如下:

-XX:StartFlightRecording=disk=true,dumponexit=true,name=profile_online,filename=/tmp/profile_`hostname`-`date +%Y_%m_%d_%H_%M_%S`.jfr,maxsize=4096m,maxage=2d,settings=/opt/app/WEB-INF/tars/prod/custom.jfc,path-to-gc-roots=false -XX:FlightRecorderOptions=maxchunksize=32m

JDK版本11.0.14,垃圾收集器为G1。启用4天后7台机器批量OOM,堆内存监控正常,但RSS从部署前15GB(峰值16GB)升至15.5GB,NMT显示Tracing指标内存开销约400MB,怀疑包含JFR内存开销。


1. 为什么OOM恰好发生在部署四天后?

你的配置中maxage=2d控制的是磁盘上保留的记录时长,但JFR运行时的内存缓存、元数据累积不受该参数直接限制。JDK11.0.14属于早期版本,存在部分JFR相关的内存泄漏或未及时回收的问题,随着运行时间增加,内存中的事件缓存、元数据会持续累积。

同时,maxsize=4096m仅限制磁盘文件总大小,不约束内存中的临时数据。当JFR的内存开销与G1垃圾收集器的本地内存占用(如大对象处理、回收阶段的额外内存)叠加后,在第4天刚好触碰到进程的内存上限(容器限制或系统ulimit),触发OOM。

2. JFR内存消耗是否与运行时长相关?

是,但正常情况下应在稳定区间波动,不会持续增长。在你的场景中出现持续增长,大概率是以下原因:

  • 事件生成速度超过刷盘速度,导致内存缓冲区持续占用内存;
  • JDK11.0.14的已知内存泄漏bug,部分JFR元数据、事件索引未被及时清理;
  • 自定义custom.jfc开启了高频事件(如细粒度方法采样、高频IO追踪),导致内存中事件数据累积过快。

3. JFR内存开销的主要来源?

JFR的本地内存开销核心来自四个部分:

  • 事件缓冲区:分为线程本地缓冲区和全局缓冲区,暂存待写入磁盘的事件,未指定bufferSize时,默认每个线程缓冲区256KB,全局缓冲区1MB左右,会根据事件生成量动态调整;
  • 元数据存储:记录类、方法、线程、字符串等事件关联元数据,避免重复写入磁盘,这部分随应用类加载、线程创建次数增长;
  • 磁盘写入缓存:配合disk=true使用,为提升写盘效率,内存中会保留待写入的32MB数据块(对应maxchunksize=32m),开销约32-64MB;
  • 追踪数据结构:如采样数据、事件索引,你已通过path-to-gc-roots=false关闭了GC根路径追踪的开销。

4. 能否基于启动参数估算其开销?

可以做大致估算,但精准度受应用负载影响:

  • 缓冲区内存:无自定义配置时,总开销约为活跃线程数*256KB + 1MB,事件生成频繁时会自动扩容;
  • 元数据内存:取决于应用类数量、线程数量,通常在几十MB到几百MB区间,类加载频繁时会显著增长;
  • 磁盘缓存:固定和maxchunksize相关,约32-64MB;
  • 注意:maxage=2d和maxsize=4096m仅控制磁盘占用,对内存开销影响极小。

临时解决方案建议

  1. 升级JDK到11.0.15或更高版本,修复已知的JFR内存泄漏问题;
  2. 检查自定义custom.jfc配置,关闭不必要的高频事件,降低事件生成量;
  3. 添加-XX:FlightRecorderOptions=old-object-queue-size=10000,避免对象追踪队列溢出导致内存增长;
  4. 定期重启应用,中断JFR内存累积过程;
  5. 监控NMT的Tracing指标,设置告警阈值,提前发现内存增长趋势。

内容的提问来源于stack exchange,提问作者WtMonster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 12:17:38