Java 怎样用 jcmd 分析本地内存增长
Java 进程的 RSS 持续上涨,并不等于 Java 堆正在泄漏。JVM 还会使用类元数据、线程、JIT 编译代码、GC 结构以及其他本地内存;这时可以在目标主机上用 jcmd 的 VM.native_memory 读取 Native Memory Tracking(NMT)报告,再用基线差异判断增长方向。
官方地址:https://docs.oracle.com/en/java/javase/25/docs/specs/man/jcmd.html
最稳妥的排查顺序是:启动时开启 NMT,先看一次 summary,再建立 baseline;经过一段真实负载后查看 summary.diff,只有分类仍不明确时才使用 detail.diff。报告里的 reserved 是地址空间预留,committed 才是已经提交给 JVM 使用的部分,两者不能直接当成同一个指标。
先把 RSS、Java 堆和 NMT 的边界分开
操作系统看到的是进程级 RSS,Java 管理接口主要描述堆和非堆内存,而 NMT 关注的是 HotSpot JVM 自身的本地内存分类。三者观察角度不同,所以“堆没有上涨但 RSS 上涨”并不是矛盾。
NMT 的启动选项有两个常用级别:summary 按 JVM 子系统汇总,detail 进一步记录调用点和虚拟内存区域。默认值是 off,也就是说进程没有在启动时打开 NMT,之后不能只靠一条 jcmd 命令补回历史数据。
在 Linux、macOS 或其他支持的环境中,诊断端与目标 JVM 应在同一台机器上,并使用启动 JVM 时相同的有效用户和组标识。容器中还要先确认 jcmd 看到了正确的 PID。
第一步:用 summary 取得可读的全局轮廓
先用轻量的摘要报告确认目标进程和大类变化,不要一上来就抓 detail。下面的命令假设进程号是 12345,命令本身只做读取和基线设置,不代表已经在本机执行过。
# 列出当前用户可见的 Java 进程,先确认 PID 与主类 jcmd -l # 查看 JVM 当前的 NMT 摘要,重点关注各分类的 reserved 和 committed jcmd 12345 VM.native_memory summary # 在可复查的时间点保存 NMT 基线,后续 diff 会与它比较 jcmd 12345 VM.native_memory baseline
摘要通常会出现 Java Heap、Class、Thread、Code、GC、Compiler、Internal、Other 和 Native Memory Tracking 等分类。分类名称会随 JDK 发布变化,因此应先看目标 JVM 实际返回的报告,不要把某一版样例当成固定协议。

第二步:按 reserved 和 committed 判断增长属于哪一层
阅读报告时先看总量,再定位变化最大的分类。reserved 表示 JVM 为某类内存预留的地址空间,committed 表示已经提交、可被使用的内存。地址空间预留变大不一定代表物理内存同步增加;如果 RSS 与 committed 同时上涨,才更值得继续追踪。
| 观察项 | 更适合回答的问题 | 不要直接得出的结论 |
|---|---|---|
| Java Heap | 堆相关的占用是否随负载上升 | 不能代表全部进程内存 |
| Class / Metaspace 相关项 | 类加载、类元数据是否持续增长 | 不能单凭一行判断类泄漏 |
| Thread | 线程结构及相关本地内存是否增加 | 不能把它等同于线程栈上限 |
| Code / Compiler | 编译代码和编译器区域是否变化 | 不能直接推断 JIT 有故障 |
| Other 或未覆盖部分 | 是否需要转向进程级或第三方库分析 | 不能把未知项归咎于 Java 堆 |
举例说,如果 Java Heap 基本稳定,但 Thread 与 Class 持续上升,应优先检查线程创建回收、类加载器生命周期和动态生成类;如果 NMT 分类变化很小而 RSS 明显增加,则要把 JNI、直接缓冲区、内存映射文件或第三方 native 库列入下一层排查。NMT 不会替你覆盖所有第三方原生分配。
第三步:用 summary.diff 把一次增长变成可比较的证据
单次 summary 只能告诉你当前状态,无法说明“从什么时候开始增加”。建立 baseline 后,等待一段与问题相关的真实业务负载,再读取差异报告:
# 读取各 JVM 分类相对 baseline 的变化,先判断增长方向 jcmd 12345 VM.native_memory summary.diff # 如果分类级别仍无法定位,再查看调用点与虚拟内存区域的差异 jcmd 12345 VM.native_memory detail.diff
summary.diff 适合回答“哪一类增长最多”,detail.diff 适合继续回答“哪些调用点或区域贡献了增长”。对于增长很小的泄漏,观察窗口需要足够长,否则差值可能被噪声覆盖。每次采集都应记录 JVM 启动参数、PID、采集时间、负载阶段和报告原文,方便将差异与发布或流量变化对齐。

第四步:在 summary 和 detail 之间做架构选择
如果目标是长期观察生产进程,通常先考虑 summary;如果目标是短时间内定位疑似调用点,再评估 detail 的成本。Oracle 的排障资料说明,开启 NMT 会带来额外开销,detail 还会记录更多分配信息,因此不能把它当成没有代价的常驻开关。
# 只按 JVM 子系统跟踪,适合先建立分类级基线 java -XX:NativeMemoryTracking=summary -jar app.jar # 需要调用点和虚拟区域信息时使用 detail,但应评估监控成本 java -XX:NativeMemoryTracking=detail -jar app.jar
这两个选项必须在 JVM 启动时决定。若当前进程是以 off 启动,不能事后用 jcmd 让它产生过去的 NMT 轨迹;实际排查往往需要安排一次带有 NMT 的受控重启,并在重启前明确采集窗口。
第五步:把报告落到一份排查清单
- 先确认 jcmd、目标 JVM 和有效用户/组匹配,避免把连接失败误判成内存没有变化。
- 记录启动参数,确认 NMT 是
summary还是detail,并记下 JVM 版本。 - 执行一次 summary,保存总量、主要分类、reserved 和 committed。
- 建立 baseline,在同一业务负载下等待足够时间,再执行 summary.diff。
- 只有分类级结论不够时再看 detail.diff,并把调用点与发布、线程、类加载或 native 依赖变化关联起来。
- 若 RSS 增长与 NMT 变化不匹配,转向直接缓冲区、JNI、映射文件、第三方 native 库和操作系统级工具,不要继续只调大堆。
常见问题
为什么 summary 中的 committed 变大,RSS 却没有等量变大?
committed 是 JVM 已提交的内存视角,RSS 还会受到操作系统回收、共享页、映射方式和进程其他区域影响。它们应放在同一采集窗口内比较,而不是逐行做一比一换算。
为什么 detail.diff 没有找到第三方库的泄漏?
NMT 重点跟踪 HotSpot JVM 的本地内存,不是所有 JNI 或第三方 native 分配都能归类。若 NMT 稳定而 RSS 继续增长,应该把调查范围移到相应库的分配接口和操作系统级进程观测。
baseline 可以重复建立吗?
可以,但每次重建都会改变后续 diff 的比较起点。记录“谁在什么时候建立了基线”很重要,否则两份差异报告可能没有可比性。
总结
用 jcmd 分析本地内存增长的关键,不是记住一条命令,而是把进程级 RSS、JVM 分类和 NMT 差异报告分层。先用 summary 建立轮廓,再用 baseline 与 summary.diff 找增长类别,最后才用 detail.diff 追调用点;当这些结果解释不了 RSS 时,明确承认 NMT 的覆盖边界,转向第三方 native 和操作系统层面。
Java ObjectInputFilter 怎样限制反序列化对象
- 上一篇
- Java ObjectInputFilter 怎样限制反序列化对象
- 下一篇
- Python SharedMemory 的 close 和 unlink 有什么区别
-
- 文章 · java教程 | 1小时前 | Java · java 反序列化 ObjectInputStream ObjectInputFilter
- Java ObjectInputFilter 怎样限制反序列化对象
- 307浏览 收藏
-
- 文章 · java教程 | 1小时前 | 数据库 · jdbc · Java教程 · java jdbc 批量插入 PreparedStatement 生成键 getGeneratedKeys
- Java JDBC 怎样读取批量插入生成键
- 341浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java Stream 怎样合并两路聚合结果
- 327浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java 时间格式兼容多个输入格式的方法
- 501浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java ·
- Java 结构化并发怎样处理子任务失败
- 208浏览 收藏
-
- 文章 · java教程 | 1小时前 | 字符集 · Java教程 · HTTP客户端 · java httpclient charset BodyHandlers ofByteArray ofString
- Java HttpClient 怎样按字节和字符串选择响应处理器
- 429浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java Optional.stream 怎样把可选值接入 Stream 管道
- 170浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java sealed interface 跨包继承限制怎样理解
- 265浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java record 紧凑构造器怎样校验参数
- 293浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java computeIfAbsent 递归更新时怎样避免死循环
- 207浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java教程 · 接口设计 · java 有序集合 SequencedCollection reversed 首尾元素
- Java 有序集合怎样统一访问首尾元素
- 308浏览 收藏
-
- 文章 · java教程 | 11小时前 | Java · 并发编程 · 虚拟线程 · java future ExecutorService 虚拟线程
- Java 虚拟线程执行器的生命周期管理
- 140浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 510次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 570次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 596次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 548次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 364次使用
-
- Go Java 算法之字符串解码示例详解
- 2023-01-07 479浏览
-
- Go Java算法之单词搜索示例详解
- 2022-12-30 337浏览
-
- Gojava算法之括号生成示例详解
- 2023-02-22 128浏览
-
- GoJava算法之累加数示例详解
- 2023-01-07 149浏览
-
- GoJava算法最大单词长度乘积示例详解
- 2023-01-12 202浏览
