Java进程中Committed Memory与RSS内存差异排查问询
解析JVM进程RES与NMT内存统计的差异
你遇到的这个问题其实很常见——top显示的进程物理内存(RES)和Native Memory Tracking(NMT)统计的已提交内存差异巨大,核心原因在于两者的统计范围完全不同,再加上老版本JDK的一些特性限制,具体可以从这几个方向拆解:
1. top RES统计的是整个进程的物理内存使用,包含大量NMT不追踪的区域
NMT只负责统计JVM内部主动管理的内存,但top的RES是操作系统视角下,进程实际占用的物理内存总和,包含这些NMT不会统计的部分:
- JVM和系统共享库:比如
libjvm.so、libc.so这些核心库,本身就可能占用几百MB的物理内存,会被算进RES,但NMT不会把它们纳入统计。 - 第三方JNI本地内存:如果你的应用或Jetty依赖了JNI库(比如某些高性能数据库驱动、加密组件),这些库直接调用系统
malloc分配的内存,NMT默认不会追踪——除非这些库主动使用了JVM提供的内存分配接口。 - 内存映射文件:Jetty加载的静态资源、日志文件或者某些依赖库的内存映射区域,都会占用物理内存,这部分也不在NMT的统计范围内。
2. 老版本JDK的NMT存在统计遗漏
你使用的JDK 1.8.0_112是2016年的旧版本,这个时期的NMT还存在不少统计盲区:
- 部分内部缓存、临时内存区域没被正确归类到NMT的统计项中;
- 早期JDK 8对直接内存的统计不够全面,可能漏掉了某些场景下的直接缓冲区分配;
- 一些JVM内部线程的内存开销统计有偏差。
3. 内存共享页的计算差异
top的RES会包含进程占用的共享内存页(比如多个JVM进程共享的libjvm.so页),虽然实际物理内存是多个进程共享的,但top默认会把整页大小算到单个进程的RES里(不同系统的top实现可能有差异),而NMT完全不统计这部分共享内存。
排查建议
想要定位具体哪部分内存导致了差异,可以按这几步来:
- 用
pmap拆解进程内存:执行pmap -x <你的进程PID>,查看输出中的内存映射区域。重点关注anon(匿名内存,对应堆、JNI分配的内存等)和file(文件映射)的大小,对比NMT的统计结果,就能找出NMT没覆盖的大内存区域。 - 查看NMT的详细输出:运行
jcmd <PID> VM.native_memory detail,仔细检查Internal、Other这类模糊分类的内存占用,有些未明确归类的JVM内部内存会放在这里。 - 排查JNI依赖:用
lsof -p <PID>列出进程加载的所有本地库,逐一检查这些库的文档或源码,看是否存在大量本地内存分配的逻辑。 - 升级JDK版本:把JDK升级到JDK 8u的较新版本(比如u372),新版本修复了很多NMT的统计bug,能让统计结果更准确,也可能缩小和RES的差异。
内容的提问来源于stack exchange,提问作者Abhishek Kumar
相关产品推荐
相关产品推荐

