必须用 in-process 模式启动 JMX Exporter,因独立进程模式存在 RMI 端口拦截、无健康检查、资源开销大等问题;javaagent 参数格式、端口、配置路径须严格正确;config.yml 需精简规则防指标爆炸;Grafana 须按 instance 或 job 标签隔离;配置不支持热更新,需重启或镜像固化。

能直接做,但必须用 in-process 模式启动 JMX Exporter,否则指标延迟高、稳定性差、还多一层运维负担。
为什么不能用独立进程模式跑 JMX Exporter
独立进程模式需要 JVM 开 RMI 端口 + JMX Exporter 主动连接 + 额外进程保活,实际落地时问题集中在这几处:
-
JMX remote port在容器或 K8s 中常被网络策略拦截,Connection refused是最常见错误 - 独立进程自身无健康检查,
jmx_exporter进程挂了,Prometheus 仍会持续 scrape,返回 200 但指标全为 0,误判成“JVM 正常” - 每个 Java 实例配一个 exporter 进程,100 个 Pod 就要起 100 个额外进程,资源开销和监控复杂度陡增
- 官方文档明确标注
in-process mode is the recommended way,不是建议,是事实上的唯一可靠路径
javaagent 启动参数必须带端口和配置路径
启动命令里 -javaagent 的格式不能错,顺序、分隔符、路径都影响加载成败:
- 完整格式是:
-javaagent:/path/to/jmx_prometheus_javaagent-0.20.0.jar=8080:/path/to/config.yml - 等号
=前后**不能有空格**,否则 JVM 直接忽略该参数,jps -lvm里根本看不到 agent 加载痕迹 - 端口号(如
8080)是 exporter 自己监听的 HTTP 端口,和应用主端口无关;若端口被占,会报Address already in use并静默退出 - 配置文件路径必须是容器内绝对路径,挂载进来的 ConfigMap 或 Secret 也要确保权限为
644,否则 agent 初始化失败,日志里只有一行Failed to initialize JMX collector
config.yml 规则写太宽泛会导致指标爆炸
用 - pattern: ".*" 看似省事,实则埋雷:
- Prometheus 抓取一次可能返回 5000+ 指标,单实例内存占用飙升,
scrape_duration_seconds超过 10s 很常见 - 很多 MBean 名含动态 ID(如
Catalina:type=ThreadPool,name="http-nio-8080"),每重启一次线程池名就变,导致 series 数量不可控,TSDB 存储压力剧增 - 真正关键的指标其实就几十个:比如
jvm_memory_bytes_used{area="heap"}、jvm_threads_current、jvm_gc_collection_seconds_count - 推荐从官方
tomcat.yml或hadoop.yml配置起步,删掉不用的whitelistObjectNames和rules,保留堆内存、GC、线程、类加载四类即可
Grafana 仪表盘要区分 JVM 实例维度
一个 Prometheus 实例通常抓多个 Java 服务,Dashboard 里不加标签过滤,所有指标会混在一起:
- 必须用
instance或自定义标签(如job="order-service"、env="prod")做变量下拉筛选 - 关键图表如 “堆内存使用率”,公式应为:
100 * jvm_memory_bytes_used{area="heap"} / jvm_memory_bytes_max{area="heap"},否则max缺失时结果为 NaN - GC 次数突增告警建议用:
rate(jvm_gc_collection_seconds_count[5m]) > 0.1,比绝对值更灵敏;注意排除name="Copy"这类新生代 minor GC 的干扰 - 别依赖单一指标,比如只看
jvm_threads_current,要叠加jvm_threads_daemon和jvm_threads_peak对比,才能判断是否线程泄漏
最容易被跳过的其实是配置文件热更新——JMX Exporter 的 config.yml 不支持运行时重载,改完必须重启 Java 进程。线上不敢轻易重启?那就得把 config.yml 打进镜像,或者用 initContainer 预校验配置语法,否则上线即故障。

















