前几天写过一篇关于排查Flink写ES作业堆外内存OOM故障分析的文章,其中提到堆外内存的跟踪监控方法,有朋友微信联系咨询相关技术。本文总结了大数据场景经常遇到的堆外内存溢出用到的五种常用跟踪监控方法的实践记录,希望能够帮助开发者诊断和优化JVM 内存性能。
本地内存跟踪(Native Memory Tracking,NMT)就是JVM支持的特性参数-XX:NativeMemoryTracking,可选值为off|sumary|detail,默认值为off。开启方法如下:
$ java -XX:NativeMemoryTracking=summary -Xms300m -Xmx300m -XX:+UseG1GC -jar app.jar
查找上述进程pid,然后通过jcmd命令跟踪该进程堆外内存情况:
$ jcmd <pid> VM.native_memory
如图所示为部分监控项内容,其中,Internal对应的就是DirectMemory占用的内存,可以通过-XX:MaxDirectMemorySize限制其上限值。
jconsole是符合JMX规范的监控工具,无需额外安装,仅需JDK即可使用,支持实时监控内存、线程、CPU 消耗及 MBeans 等信息。要运行 jconsole,请从 JDK 的 bin 文件夹中执行以下命令:
<JDK_PATH>\bin\jconsole
在启动界面选择本地或远程 Java 进程,如图所示:
在“Memory”选项卡中选择“Non-Heap Memory Usage”图表,实时查看非堆内存使用趋势,如图所示:
VisualVM
VisualVM 是功能更强大的可视化 JVM 监控工具,支持 CPU 负载、内存使用、线程活动监控及堆转储分析,还可通过插件扩展功能。在 VisualVM 的“Monitor”选项卡中,可直接查看Metaspace(元空间)的大小,如图所示:
若需监控其他非堆区域,需安装插件,插件地址如下:
https://visualvm.github.io/plugins.html
安装MBeans插件后,在“MBeans”选项卡中可查看整体及特定非堆内存区域的使用情况,如图所示:
安装Buffer Monitor插件后,在“Buffer Pools”选项卡中可查看直接缓冲区(Direct Buffer)和内存映射文件的使用情况,如图所示:
JMC,即Java Mission Control,是专业JVM监控与诊断工具,支持性能分析、内存监控、线程检查及垃圾回收行为分析,还可通过飞行记录(Flight Recording)深入追踪内存问题。
连接应用的 MBean Server 后,在“MBeans Browser”中可查看非堆内存总占用(不包含直接缓冲区),如图所示:
在“BufferPool”组中可查看直接缓冲区的使用情况,如图所示:
从Java 20开始,可通过JFR持续记录NMT数据:
java -XX:NativeMemoryTracking=detail -XX:StartFlightRecording=name=Profiling,filename=nmt-recording.jfr,settings=profile -jar path/ourapp.jar
使用JMC打开飞行记录文件后,在“Event Browser”中可查看Total Native Memory Usage:包含堆和非堆的总提交内存,如图所示:
也可以查看Native Memory Usage Per Type:非堆内存各类型的详细占用情况,如图所示:
JMX-DExporter是一款以与Prometheus兼容的格式公开JMX指标的工具。可以收集JVM指标,例如内存使用情况、线程活动、垃圾回收统计信息等。需要下载JAR包,设置配置文件 jmx_exporter_config.yml:
jmx_exporter_config.yml:
startDelaySeconds: 0
lowercaseOutputName: true
lowercaseOutputLabelNames: true
rules:
- pattern: "java.lang:type=Memory"
name: "jvm_memory_usage_bytes"
labels:
area: "$2"
type: GAUGE
启动进程即可:
java -javaagent:.path-to-agent-jar\jmx_prometheus_javaagent.jar=port:path-to-agent-jar\jmx_exporter_config.yml -jar .path-to-app\app.jar
然后,修改Prometheus配置文件prometheus.yml:
scrape_configs:
- job_name: "our_app"
static_configs:
- targets: ["localhost:port"]
重启Prometheus,即可查看采集到的内存信息,如图所示:
本文介绍五种JVM非堆内存监控工具,适用于不同场景:
简单场景:若只需基础监控,使用jcmd或jconsole。
深度分析:若需详细持续监控特定内存类型,使用VisualVM插件或JMC。
云原生集成:若需将监控纳入Prometheus,JMX-DExporter是最佳选择。
注意:所有工具均会产生额外资源消耗,需根据实际场景评估性能影响,合理选择监控粒度和工具组合。