当前位置:首页 > 文章列表 > 文章 > java教程 > Java 怎样用 jcmd 分析本地内存增长

Java 怎样用 jcmd 分析本地内存增长

来源:17golang原创 2026-10-18 01:15:46 0浏览 收藏

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 实际返回的报告,不要把某一版样例当成固定协议。

jcmd 与 Java 进程、reserved、committed 及 JVM 分类的静态关系说明图
图1:jcmd 读取 JVM 的 NMT 摘要,并把 reserved、committed 与主要子系统放在同一张静态说明图中;这不是运行截图。

第二步:按 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、采集时间、负载阶段和报告原文,方便将差异与发布或流量变化对齐。

Native Memory Tracking 基线差异与排查结论的静态关系说明图
图2:baseline 与当前快照通过 summary.diff、detail.diff 形成差异报告,再落到增长分类和排查结论;这不是运行截图。

第四步:在 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 的受控重启,并在重启前明确采集窗口。

第五步:把报告落到一份排查清单

  1. 先确认 jcmd、目标 JVM 和有效用户/组匹配,避免把连接失败误判成内存没有变化。
  2. 记录启动参数,确认 NMT 是 summary 还是 detail,并记下 JVM 版本。
  3. 执行一次 summary,保存总量、主要分类、reserved 和 committed。
  4. 建立 baseline,在同一业务负载下等待足够时间,再执行 summary.diff。
  5. 只有分类级结论不够时再看 detail.diff,并把调用点与发布、线程、类加载或 native 依赖变化关联起来。
  6. 若 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 和操作系统层面。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java ObjectInputFilter 怎样限制反序列化对象Java ObjectInputFilter 怎样限制反序列化对象
上一篇
Java ObjectInputFilter 怎样限制反序列化对象
Python SharedMemory 的 close 和 unlink 有什么区别
下一篇
Python SharedMemory 的 close 和 unlink 有什么区别
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    510次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    570次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    596次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    548次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    364次使用