You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java内存使用量远超Native Memory Tracking报告值排查求助

排查Spring Boot应用内存占用远超NMT报告的问题

我在优化Spring Boot应用内存占用时,发现实际内存使用量远超预期。使用Java的Native Memory Tracking(NMT)功能检测后,报告显示已提交内存为601040KB(约600MB):

> jcmd 1 VM.native_memory
1:

Native Memory Tracking:

(Omitting categories weighting less than 1KB)

Total: reserved=18460740KB, committed=601040KB
       malloc: 97616KB #503895
       mmap:   reserved=18363124KB, committed=503424KB

-                 Java Heap (reserved=16465920KB, committed=262144KB)
                            (mmap: reserved=16465920KB, committed=262144KB)
[...]

但top、ps等系统工具报告的内存使用量要高得多(约1GB驻留集大小/约1.3GB内存大小):

> ps -e -o pid,pmem,rss,size,cmd
    PID %MEM   RSS  SIZE CMD
      1  1.6 1061716 1311316 java org.springframework.boot.loader.JarLauncher
[...]

我在NMT文档中找到的线索:

由于NMT不跟踪非JVM代码的内存分配,您可能需要使用操作系统支持的工具来检测原生代码中的内存泄漏。

但我不确定哪些非JVM代码会占用数百MB内存,有没有办法找出内存占用来源?


额外信息

VM.flags输出

> jcmd 1 VM.flags
1:
-XX:CICompilerCount=12 -XX:ConcGCThreads=3 -XX:G1ConcRefinementThreads=13 -XX:G1EagerReclaimRemSetThreshold=64 -XX:G1HeapRegionSize=8388608 -XX:G1PeriodicGCInterval=60000 -XX:G1RemSetArrayOfCardsEntries=64 -XX:G1RemSetHowlMaxNumBuckets=8 -XX:G1RemSetHowlNumBuckets=8 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=1056964608 -XX:MarkStackSize=4194304 -XX:MaxHeapFreeRatio=20 -XX:MaxHeapSize=16861102080 -XX:MaxNewSize=10116661248 -XX:MinHeapDeltaBytes=8388608 -XX:MinHeapFreeRatio=5 -XX:MinHeapSize=268435456 -XX:NativeMemoryTracking=detail -XX:NonNMethodCodeHeapSize=7602480 -XX:NonProfiledCodeHeapSize=122027880 -XX:+PrintGC -XX:ProfiledCodeHeapSize=122027880 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:SoftMaxHeapSize=268435456 -XX:-THPStackMitigation -XX:+UseCompressedOops -XX:+UseG1GC

VM.system_properties输出

> jcmd 1 VM.system_properties
1:
#Thu Feb 08 11:52:42 CET 2024
CONSOLE_LOG_CHARSET=UTF-8
FILE_LOG_CHARSET=UTF-8
PID=1
catalina.base=/tmp/tomcat.8080.1256868023389071013
catalina.home=/tmp/tomcat.8080.1256868023389071013
catalina.useNaming=false
file.encoding=UTF-8
file.separator=/
java.awt.headless=true
java.class.path=.
java.class.version=65.0
java.home=/opt/java/openjdk
java.io.tmpdir=/tmp
java.library.path=/usr/java/packages/lib\:/usr/lib64\:/lib64\:/lib\:/usr/lib
java.runtime.name=OpenJDK Runtime Environment
java.runtime.version=21.0.1+12-LTS
java.specification.name=Java Platform API Specification
java.specification.vendor=Oracle Corporation
java.specification.version=21
java.vendor=Eclipse Adoptium
java.vendor.url=https\://adoptium.net/
java.vendor.url.bug=https\://github.com/adoptium/adoptium-support/issues
java.vendor.version=Temurin-21.0.1+12
java.version=21.0.1
java.version.date=2023-10-17
java.vm.compressedOopsMode=Zero based
java.vm.info=mixed mode, sharing
java.vm.name=OpenJDK 64-Bit Server VM
java.vm.specification.name=Java Virtual Machine Specification
java.vm.specification.vendor=Oracle Corporation
java.vm.specification.version=21
java.vm.vendor=Eclipse Adoptium
java.vm.version=21.0.1+12-LTS
jdk.debug=release
line.separator=\n
native.encoding=UTF-8
os.arch=amd64
os.name=Linux
os.version=5.18.13-200.fc36.x86_64
path.separator=\:
spring.beaninfo.ignore=true
stderr.encoding=UTF-8
stdout.encoding=UTF-8
sun.arch.data.model=64
sun.boot.library.path=/opt/java/openjdk/lib
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
sun.java.command=org.springframework.boot.loader.JarLauncher
sun.java.launcher=SUN_STANDARD
sun.jnu.encoding=UTF-8
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
user.country=US
user.dir=/application
user.home=/root
user.language=en
user.name=root
user.timezone=Europe/Berlin

排查非JVM内存占用的实用方法

  • 用pmap分析内存映射:执行pmap -x <PID>,查看进程的内存映射详情,重点关注anon类型的大内存区域,以及加载的共享库(.so文件)占用的内存。如果某个共享库或匿名映射占用异常大的内存,大概率是问题来源。
  • 通过perf跟踪内存分配:运行perf record -g -p <PID>记录进程的内存分配调用栈,再用perf report分析,找出频繁分配内存的原生函数,定位到对应的库或代码。
  • 检查JNI调用和第三方库:Spring Boot应用依赖的第三方库(如数据库驱动、序列化库、原生扩展)常通过JNI分配内存,排查这些库的使用情况,尤其是是否存在内存泄漏。
  • 区分共享内存与独占内存:系统工具报告的RSS包含进程共享的内存页(如共享库、文件缓存),用smem -t -k -p查看进程的实际独占内存(USS),明确哪些是进程自身占用的内存。
  • 对比NMT的summary和detail输出:执行jcmd <PID> VM.native_memory summary和detail,确认NMT已跟踪的所有JVM内存类别,排除JVM内部未被正确统计的部分。

内容的提问来源于stack exchange,提问作者Nikos Epping

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 08:45:05