news 2026/5/16 21:35:11

虾皮Java面试被问:JVM Native Memory Tracking追踪堆外内存泄漏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
虾皮Java面试被问:JVM Native Memory Tracking追踪堆外内存泄漏

JVM Native Memory Tracking (NMT) 追踪堆外内存泄漏

📊NMT基础概念

什么是NMT?

Native Memory Tracking是HotSpot JVM内置的追踪工具,用于监控JVM内部的内存使用情况。

监控范围

bash

复制

下载

# NMT追踪的内存区域: 1. Java Heap # Java堆 2. Class # 类元数据 3. Thread # 线程栈 4. Code # JIT编译代码 5. GC # 垃圾收集器 6. Compiler # 编译器 7. Internal # 命令行解析、JVMTI等 8. Symbol # 符号表 9. Native Memory Tracking # NMT自身开销 10. Arena Chunk # 分配器竞技场 11. Test # 测试代码 12. Other # 未分类内存

🔧NMT启用与配置

启动参数配置

bash

复制

下载

# 基础启用 java -XX:NativeMemoryTracking=summary -jar app.jar # 详细模式(提供更多细节) java -XX:NativeMemoryTracking=detail -jar app.jar # 基准模式(记录初始内存使用) java -XX:NativeMemoryTracking=summary -XX:+UnlockDiagnosticVMOptions \ -XX:+PrintNMTStatistics -jar app.jar

运行时监控命令

bash

复制

下载

# 1. 获取当前内存使用情况 jcmd <pid> VM.native_memory summary # 2. 获取详细内存使用情况 jcmd <pid> VM.native_memory detail # 3. 获取按类别统计 jcmd <pid> VM.native_memory summary scale=MB # 4. 基线测量(记录当前状态作为基准) jcmd <pid> VM.native_memory baseline # 5. 对比差异(与基线对比) jcmd <pid> VM.native_memory summary.diff # 6. 详细差异对比 jcmd <pid> VM.native_memory detail.diff # 7. 按类别显示差异 jcmd <pid> VM.native_memory summary.diff scale=KB

🔍常见堆外内存泄漏场景

1.Direct ByteBuffer泄漏

java

复制

下载

// 常见的DirectByteBuffer泄漏代码 public class DirectMemoryLeak { private List<ByteBuffer> bufferList = new ArrayList<>(); public void leakMemory() { // 每次分配100MB直接内存 ByteBuffer buffer = ByteBuffer.allocateDirect(100 * 1024 * 1024); bufferList.add(buffer); // 忘记清理 } // 正确做法:需要显式释放 public void cleanDirectBuffer(ByteBuffer buffer) { if (buffer.isDirect()) { // 需要等GC回收或手动调用Cleaner ((DirectBuffer) buffer).cleaner().clean(); } } }

2.MappedByteBuffer泄漏

java

复制

下载

public class MappedBufferLeak { private List<MappedByteBuffer> mappedBuffers = new ArrayList<>(); public void mapFile(String filePath) throws IOException { RandomAccessFile file = new RandomAccessFile(filePath, "rw"); FileChannel channel = file.getChannel(); // 内存映射,会占用Native Memory MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_WRITE, 0, channel.size() ); mappedBuffers.add(buffer); // 忘记关闭:channel.close() 和 file.close() } }

3.JNI代码内存泄漏

java

复制

下载

public class JNIMemoryLeak { static { System.loadLibrary("nativeLib"); } // Native方法可能分配内存 public native void allocateNativeMemory(long size); public native void freeNativeMemory(); public void causeLeak() { allocateNativeMemory(1024 * 1024); // 分配1MB // 忘记调用freeNativeMemory() } }

4.线程创建过多

java

复制

下载

public class ThreadLeak { private ExecutorService executor = Executors.newCachedThreadPool(); public void createThreads() { for (int i = 0; i < 10000; i++) { executor.submit(() -> { try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } }); } // 每个线程栈占用1MB(默认),10k线程占用10GB Native Memory } }

📈NMT输出解析

示例输出分析

text

复制

下载

Native Memory Tracking: Total: reserved=6643041KB, committed=397016KB - Java Heap (reserved=5070848KB, committed=317440KB) (mmap: reserved=5070848KB, committed=317440KB) - Class (reserved=1070513KB, committed=12609KB) (classes #1199) (malloc=9329KB #1525) (mmap: reserved=1067184KB, committed=3280KB) - Thread (reserved=26546KB, committed=26546KB) (thread #26) (stack: reserved=26432KB, committed=26432KB) (malloc=96KB #52) (arena=18KB #50) - Code (reserved=249632KB, committed=2592KB) (malloc=32KB #299) (mmap: reserved=249600KB, committed=2560KB) - GC (reserved=47739KB, committed=47739KB) (malloc=10423KB #117) (mmap: reserved=37316KB, committed=37316KB) - Compiler (reserved=132KB, committed=132KB) (malloc=1KB #21) (arena=131KB #3) - Internal (reserved=580KB, committed=580KB) (malloc=548KB #651) (mmap: reserved=32KB, committed=32KB) - Symbol (reserved=1525KB, committed=1525KB) (malloc=976KB #111) (arena=549KB #1) - Native Memory Tracking (reserved=161KB, committed=161KB) (malloc=99KB #1561) (tracking overhead=62KB) - Arena Chunk (reserved=175KB, committed=175KB) (malloc=175KB) - Unknown (reserved=33KB, committed=33KB) (mmap: reserved=33KB, committed=33KB)

关键指标解读

bash

复制

下载

# 重点监控区域: 1. Thread区域持续增长 → 线程泄漏 2. Internal区域异常大 → 可能DirectByteBuffer泄漏 3. GC区域过大 → GC配置问题 4. Arena Chunk异常增长 → 内存池泄漏

🚨堆外内存泄漏诊断流程

步骤1:启用NMT监控

bash

复制

下载

# 启动应用 java -XX:NativeMemoryTracking=detail \ -XX:+UnlockDiagnosticVMOptions \ -XX:+PrintNMTStatistics \ -jar application.jar # 获取进程ID jps -l # 建立基线 jcmd <pid> VM.native_memory baseline

步骤2:模拟操作并收集数据

bash

复制

下载

# 执行疑似泄漏的操作 curl http://localhost:8080/leaky-endpoint # 等待一段时间 sleep 30 # 获取差异报告 jcmd <pid> VM.native_memory summary.diff

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】​​​

步骤3:分析泄漏点

bash

复制

下载

# 详细差异分析 jcmd <pid> VM.native_memory detail.diff > nmt_diff.txt # 重点关注增长的区域 grep -A 5 "Total: " nmt_diff.txt grep "committed" nmt_diff.txt | sort -k2 -nr

步骤4:使用pmap进一步分析

bash

复制

下载

# 查看进程内存映射 pmap -x <pid> | sort -k2 -nr | head -20 # 查看anon内存段 pmap <pid> | grep anon | awk '{sum+=$2} END {print sum}'

🔧高级诊断技巧

结合其他工具分析

bash

复制

下载

# 1. 使用gdb分析Native Memory gdb -p <pid> (gdb) malloc_info 0 /tmp/malloc_info.xml # 2. 使用strace追踪系统调用 strace -f -e trace=mmap,munmap,brk -p <pid> 2>&1 | grep -v ENOENT # 3. 使用/proc文件系统 cat /proc/<pid>/maps | grep heap # 查看堆内存 cat /proc/<pid>/smaps | grep -A 15 "heap" # 详细堆信息 # 4. 监控系统内存 watch -n 1 'ps aux | grep java | grep -v grep'

自动化监控脚本

bash

复制

下载

#!/bin/bash # monitor_nmt.sh PID=$1 INTERVAL=10 OUTPUT_DIR="./nmt_logs" mkdir -p $OUTPUT_DIR while true; do TIMESTAMP=$(date +%Y%m%d_%H%M%S) # 收集NMT数据 jcmd $PID VM.native_memory detail > "$OUTPUT_DIR/nmt_detail_$TIMESTAMP.txt" jcmd $PID VM.native_memory summary > "$OUTPUT_DIR/nmt_summary_$TIMESTAMP.txt" # 收集系统内存信息 ps -p $PID -o pid,rss,vsz,pcpu,pmem,cmd >> "$OUTPUT_DIR/system_mem_$TIMESTAMP.txt" pmap $PID | tail -1 >> "$OUTPUT_DIR/pmap_$TIMESTAMP.txt" # 分析增长趋势 if [ -f "$OUTPUT_DIR/nmt_summary_previous.txt" ]; then diff "$OUTPUT_DIR/nmt_summary_previous.txt" \ "$OUTPUT_DIR/nmt_summary_$TIMESTAMP.txt" \ > "$OUTPUT_DIR/diff_$TIMESTAMP.txt" fi cp "$OUTPUT_DIR/nmt_summary_$TIMESTAMP.txt" \ "$OUTPUT_DIR/nmt_summary_previous.txt" sleep $INTERVAL done

🛠️代码层面的预防措施

1.DirectByteBuffer管理类

java

复制

下载

public class DirectMemoryManager { private static final List<ByteBuffer> BUFFERS = new ArrayList<>(); private static final Cleaner CLEANER = Cleaner.create(); public static ByteBuffer allocateDirect(long size) { ByteBuffer buffer = ByteBuffer.allocateDirect((int) size); BUFFERS.add(buffer); // 注册清理操作 CLEANER.register(buffer, () -> { if (buffer.isDirect()) { ((DirectBuffer) buffer).cleaner().clean(); } }); return buffer; } public static void cleanAll() { BUFFERS.forEach(buffer -> { if (buffer.isDirect() && buffer.capacity() > 0) { ((DirectBuffer) buffer).cleaner().clean(); } }); BUFFERS.clear(); } }

2.资源关闭模板

java

复制

下载

public class ResourceTemplate { public static void withMappedBuffer(String filePath, Consumer<MappedByteBuffer> action) { RandomAccessFile file = null; FileChannel channel = null; try { file = new RandomAccessFile(filePath, "rw"); channel = file.getChannel(); MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_WRITE, 0, channel.size() ); action.accept(buffer); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (channel != null) channel.close(); if (file != null) file.close(); } catch (IOException e) { // 记录日志 } } } }

3.线程池监控

java

复制

下载

public class MonitoredThreadPool { private final ThreadPoolExecutor executor; private final ScheduledExecutorService monitor; public MonitoredThreadPool() { this.executor = new ThreadPoolExecutor( 10, 100, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000) ); // 监控线程池状态 this.monitor = Executors.newSingleThreadScheduledExecutor(); this.monitor.scheduleAtFixedRate(() -> { System.out.println("Thread pool stats:"); System.out.println(" Active threads: " + executor.getActiveCount()); System.out.println(" Pool size: " + executor.getPoolSize()); System.out.println(" Queue size: " + executor.getQueue().size()); // 检查Native Memory使用 try { Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec("pmap " + getPid()); // 解析输出... } catch (IOException e) { e.printStackTrace(); } }, 0, 30, TimeUnit.SECONDS); } private String getPid() { return ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; } }

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】​​​

📊监控告警配置

Prometheus监控指标

yaml

复制

下载

# NMT监控指标采集 - job_name: 'jvm_nmt' static_configs: - targets: ['localhost:12345'] metrics_path: /nmt/metrics params: pid: ['<java_pid>']

Grafana仪表板配置

json

复制

下载

{ "panels": [ { "title": "Native Memory Usage", "targets": [ { "expr": "jvm_nmt_memory_reserved{region=\"Internal\"}", "legendFormat": "Internal Memory" }, { "expr": "jvm_nmt_memory_committed{region=\"Thread\"}", "legendFormat": "Thread Stack" } ] } ] }

🚨紧急处理方案

内存泄漏紧急处理

bash

复制

下载

# 1. 立即收集证据 jcmd <pid> VM.native_memory detail > emergency_nmt_$(date +%s).txt jstack <pid> > emergency_threads_$(date +%s).txt # 2. 尝试清理DirectBuffer jmap -histo:live <pid> | grep DirectByteBuffer # 3. 如果可能,触发Full GC jcmd <pid> GC.run # 4. 限制Native Memory使用 # 重启时添加参数 -XX:MaxDirectMemorySize=512m # 限制直接内存 -XX:ThreadStackSize=256k # 减小线程栈大小 -XX:MetaspaceSize=128m # 限制元空间

📝最佳实践总结

预防措施

  1. 定期监控:生产环境开启NMT,定期收集数据

  2. 代码审查:重点审查DirectByteBuffer、MappedByteBuffer使用

  3. 资源管理:使用try-with-resources或模板方法

  4. 容量规划:合理设置JVM参数限制Native Memory

  5. 压力测试:模拟长时间运行,观察Native Memory增长

诊断要点

  1. 基线对比:使用baseline和diff功能

  2. 趋势分析:关注committed内存的持续增长

  3. 区域定位:确定是Thread、Internal还是Arena泄漏

  4. 结合工具:NMT+pmap+jstack综合分析

调优建议

bash

复制

下载

# 推荐配置 -XX:NativeMemoryTracking=summary # 生产环境开启 -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics # JVM退出时打印统计 -XX:MaxDirectMemorySize=1g # 限制直接内存 -XX:ThreadStackSize=512k # 优化线程栈大小 -XX:MetaspaceSize=256m # 设置合适的元空间 -XX:MaxMetaspaceSize=512m # 限制最大元空间

通过系统化的NMT监控和诊断,可以有效发现和解决堆外内存泄漏问题,保证Java应用的稳定运行。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/14 8:58:15

应急管理物资调配:MGeo优化仓库选址决策

应急管理物资调配&#xff1a;MGeo优化仓库选址决策 在重大自然灾害或突发公共事件中&#xff0c;应急物资的快速响应与精准投送是保障人民生命财产安全的关键。其中&#xff0c;仓库选址决策作为整个应急物流体系的核心环节&#xff0c;直接影响物资调度效率、运输成本和覆盖范…

作者头像 李华
网站建设 2026/5/9 9:09:26

Z-Image-Turbo编程教学辅助:算法流程图、数据结构图生成

Z-Image-Turbo编程教学辅助&#xff1a;算法流程图、数据结构图生成 引言&#xff1a;AI图像生成如何赋能编程教学&#xff1f; 在现代软件工程与计算机教育中&#xff0c;可视化表达已成为理解复杂系统不可或缺的一环。无论是讲解递归调用栈、排序算法执行过程&#xff0c;还是…

作者头像 李华
网站建设 2026/5/6 16:24:35

Z-Image-Turbo民俗文化保存:节庆活动、民间艺术图像生成

Z-Image-Turbo民俗文化保存&#xff1a;节庆活动、民间艺术图像生成 引言&#xff1a;AI赋能传统文化保护的新路径 在全球化与现代化进程加速的背景下&#xff0c;许多珍贵的民俗文化元素正面临失传风险。传统节庆仪式、地方戏曲、民间手工艺等非物质文化遗产&#xff0c;往往…

作者头像 李华
网站建设 2026/4/30 22:58:40

MGeo在网约车司机注册地址审核中的应用

MGeo在网约车司机注册地址审核中的应用 引言&#xff1a;网约车场景下的地址审核挑战 随着共享出行行业的快速发展&#xff0c;网约车平台对司机注册信息的准确性要求日益提高。其中&#xff0c;司机提交的常住地址或服务区域地址是风控与合规审核的关键字段之一。然而&#xf…

作者头像 李华
网站建设 2026/5/6 18:38:52

MGeo能否识别错别字地址?具备一定容错纠错能力

MGeo能否识别错别字地址&#xff1f;具备一定容错纠错能力 引言&#xff1a;中文地址匹配的现实挑战与MGeo的定位 在真实业务场景中&#xff0c;用户输入的地址信息往往存在大量非标准化问题——错别字、简写、语序颠倒、多音字误用等。例如&#xff0c;“北京市朝阳区望京SO…

作者头像 李华
网站建设 2026/5/1 6:21:05

markdown文档友好:M2FP项目README含详细使用说明

&#x1f9e9; M2FP 多人人体解析服务 (WebUI API) &#x1f4d6; 项目简介 在计算机视觉领域&#xff0c;人体解析&#xff08;Human Parsing&#xff09; 是一项关键的细粒度语义分割任务&#xff0c;旨在将图像中的人体分解为多个语义明确的身体部位&#xff0c;如面部、头…

作者头像 李华