我们尝试在生产环境中启用 JFR(Java Flight Recorder)功能。我们使用以下启动参数:
-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。
在我们的一个应用程序上启用上述 JFR 配置后,四天后我们在 7 台机器上遇到了批量内存不足 (OOM) 问题。
我们查看了监控数据,发现堆情况显得比较正常。但是,我们注意到,在最近部署之前,mem rss 使用量约为 15GB(峰值为 16GB),部署后增加到约 15.5GB。
为了解决这个问题,我们尝试使用本机内存跟踪 (NMT) 进行分析,发现四天后,跟踪指标的内存开销已达到 400MB 左右。我们怀疑跟踪指标可能包括 JFR 的内存开销。
我们很困惑为什么部署后四天就出现批量 OOM 问题,以及 JFR 的内存消耗是否与运行时长有关。我们也很好奇 JFR 内存开销的主要来源以及是否有办法根据启动参数来估计 JFR 的内存开销。
JFR 似乎会填满内存,直到耗尽,除非您确保将录音转储到磁盘。 请注意,飞行记录数据不是从 Java 堆分配的,而是从系统内存分配的。 至少在 Java 21 中,即使您停止录制,该录制也将变得无法访问,并且仍然保留其正在使用的内存。
我通过使用
-XX:StartFlightRecording=disk=false,name=jfr
启动 Java 进程,然后运行 jcmd <PID> JfR.check
来确认录制正在进行中来对此进行测试。 让进程运行几个小时并且系统内存以恒定的速度消耗。然后jcmd <PID> JFR.stop name=jfr
。 并且系统内存停止被消耗,但没有被释放。 此时运行jcmd <PID> JfR.check
显示没有飞行记录。
要解决此问题,请安排一个任务定期运行以使用
jcmd <PID> JFR.dump
转储录音