• 登录   注册   投稿  
  • 2025-12-04 08:20:02
    64

    JVM的NMT功能到底能帮我们解决哪些内存问题?

    摘要
    你是不是也遇到过这种情况,明明给JVM设置了-Xmx堆内存参数,但用top命令一看,实际占用的内存(RSS)却远超设定值,然后就开始怀疑人生了?😵 或者程序跑着跑着内存就缓慢增长,最后要么被OOMKi...

    你是不是也遇到过这种情况,明明给JVM设置了-Xmx堆内存参数,但用top命令一看,实际占用的内存(RSS)却远超设定值,然后就开始怀疑人生了?😵 或者程序跑着跑着内存就缓慢增长,最后要么被OOMKiller干掉,要么直接卡死。这种时候,​​NMT(Native Memory Tracking)​​ 这个JDK自带的小工具,就可能成了你的救命稻草。我平常排查内存问题时,一般会先把它拎出来用。

    ​NMT到底是什么玩意儿?​

    简单来说,NMT(Native Memory Tracking)是​​从Java 7 U40版本开始引入的一个HotSpot虚拟机功能​​,专门用来跟踪JVM自身内部的内存使用情况。不过得注意,它主要管的是JVM自己申请的那些本地内存(non-heap),像是什么线程栈(Tbread)、元空间(Metaspace)、代码缓存(CodeCache)、GC相关数据结构等等,对于第三方native库(比如通过JNI调用的那些so/dll文件)里直接用malloc申请的内存,NMT目前是管不了的。这就像是,JVM给自己家的内存开销做了个精细的账本,但房东(操作系统)眼里,你整个进程(包括JVM和那些不守规矩的第三方库)占的物业总面积(RSS)可能要大得多。

    ​怎么开启和使用NMT?​

    开启NMT其实挺简单的,就是在启动JVM的时候加上那么几个参数。但这里有个关键点,​​NMT默认是关闭的,而且必须在启动JVM时通过命令行参数来开启​​,后面可不能动态打开。这点我一开始也没注意,后来查资料才明白。

    • ​开启命令​​:主要是这个参数 -XX:NativeMemoryTracking=[off | summary | detail]。通常我们排查问题会用 detail级别,虽然性能损耗可能有个5%-10%,但信息更全。

    • ​查看数据​​:JVM跑起来之后,就用JDK自带的 jcmd工具来查看。命令格式是 jcmd VM.native_memory [summary | detail]。你还可以用 jcmd VM.native_memory baseline先打个快照,过一阵子再用 jcmd VM.native_memory detail.diff对比一下,看看这段时间哪些内存区域有变化,这对查内存缓慢泄漏特别有用。

    ​NMT实战:一个内存泄漏的排查案例​

    光说不练假把式,说个我们之前碰到过的真事儿。有个线上服务,堆内存(-Xmx)只设置了6G,但用着用着物理内存占用(RSS)竟然慢慢涨到了11G以上。当时第一反应是不是堆内存泄漏了?但用jstat看GC日志,老生代使用率还挺稳定,不像是堆的问题。

    然后我们就祭出了NMT(启动时已经加了 -XX:NativeMemoryTracking=detail)。用 jcmd打出详细报告后,发现NMT跟踪到的已提交内存(committed)才7个多G,但整个进程的RSS确实11个G了。这说明啥?说明​​有相当一部分内存(大约4G)不是JVM自己申请的,是JVM之外的部分分配的​​,比如可能是程序里通过JNI调用的Native代码,或者某些JDK的Native方法直接调用了malloc之类的函数。

    接着,我们又用了Linux另一个命令 pmap -x 来看进程的完整内存映射。把NMT报告里的内存地址区间和pmap的结果一对比,发现有一大块将近7G的“匿名映射”(anon)内存,在NMT报告里找不到主儿。这块内存就非常可疑了。

    后来,结合GDB调试工具,我们最终定位到问题出在一段使用 Files.list方法遍历目录的代码上。​​Java的 Files.list方法返回的流是需要显式关闭的​​,否则就会导致底层的目录句柄(以及关联的内存)无法释放,从而造成资源泄漏。这个案例也提醒我们,使用这些NIO的API时,一定要仔细看文档,用try-with-resources语句确保资源被关闭。

    ​NMT的局限性在哪里?​

    虽然NMT挺强大,但它也不是万能的。上面也提到了,​​它管不了非JVM管理的内存分配​​,比如第三方Native库自己玩的内存。有时候,pmap看到的进程总内存和NMT报告的JVM内存对不上,除了这些“外来户”的内存,还可能跟操作系统的内存管理机制有关,比如内存延迟提交(Lazy分配)、共享内存等。所以,​​NMT和pmap、top这些系统级工具结合起来用,才能把内存问题看得更清楚​​。

    ​个人使用心得和一些小提示​

    用NMT也有一段时间了,说几点我的感受吧:

    1. ​性能开销​​:线上环境长期开启NMT(特别是detail模式)还是要谨慎,毕竟有5%-10%的性能损耗。一般是在发现问题了,或者在做性能测试、问题排查时才开启。

    2. ​结合其他工具​​:​​NMT、pmap,再加上jstat、jstack这些JDK工具,以及像MAT这样的堆转储分析工具,组合起来用才是王道​​。它们各自从不同角度反映问题。

    3. ​理解内存区域​​:看NMT报告时,得了解各个内存区域是干啥的。比如“Class”区域跟元空间(Metaspace)有关,“GC”区域是垃圾收集器自己用的数据结构,“Tbread”区域和线程数以及线程栈大小(-Xss)直接相关。这些区域的大小如果异常,能给你很重要的排查方向。

    总之吧,JVM内存问题排查确实有点烦,但有了NMT这个工具,至少我们能更清晰地看到JVM内部的内存布局了。希望上面啰嗦的这些,能帮你下次遇到内存问题时多一个排查的思路。毕竟,知道了内存都花在哪,心里才能不慌嘛。

    JVM的NMT功能到底能帮我们解决哪些内存问题?

    本文链接:https://www.ainiseo.com/btc/38932.html

    免责声明:网所有文字、图片、视频、音频等资料均来自互联网,不代表本站赞同其观点,内容仅提供用户参考,若因此产生任何纠纷,本站概不负责,如有侵权联系本站删除!
    请联系我们邮箱:207985384@qq.com
    长沙爱搜电子商务有限公司 版权所有
    备案号:湘ICP备12005316号

    声明:文章不代表爱搜币圈网观点及立场,不构成本平台任何投资建议。投资决策需建立在独立思考之上,本文内容仅供参考,风险自担!转载请注明出处!侵权必究!

    相关推荐

    最新热点

    查看更多