内存泄漏的快速定位技巧
内存泄漏是后台服务最常见的问题之一。我在生产环境中遇到过各种内存泄漏场景:Java 堆外内存泄漏、Go 的 goroutine 泄漏、C++的野指针问题等。经过多次实战,我总结了一套快速定位内存泄漏的方法。
Java 应用内存泄漏排查
堆内存泄漏
1 2 3 4 5 6 7 8 9 10
| jmap -dump:format=b,file=heap.dump <pid>
jmap -histo <pid> | head -20
|
堆外内存泄漏
1 2 3 4 5 6
| pmap -d <pid> | sort -k2 -nr | head -20
|
实战案例:某微服务运行 3 天后 OOM,堆内存正常但 RSS 持续增长。通过 pmap 发现大量 64MB 的匿名映射,最终定位到 Netty 的 DirectBuffer 未及时释放,通过调整-XX:MaxDirectMemorySize 和优化连接池解决。
Go 程序内存泄漏排查
Goroutine 泄漏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| func monitorGoroutines() { ticker := time.NewTicker(30 * time.Second) for { select { case <-ticker.C: count := runtime.NumGoroutine() log.Printf("Current goroutines: %d", count) } } }
import _ "net/http/pprof"
|
内存分配分析
1 2 3 4 5 6 7 8 9 10 11
| go tool pprof http://localhost:6060/debug/pprof/heap
(pprof) top10
(pprof) web
(pprof) list funcName
|
常见 Go 内存泄漏模式:
- Channel 未关闭导致 goroutine 阻塞
- Timer 未 stop 导致资源未释放
- 全局 map 持续增长未清理
快速排查工具集
系统级内存分析
1 2 3 4 5 6 7 8 9 10
| free -h cat /proc/meminfo
cat /proc/<pid>/status | grep -E "(VmRSS|VmSize)" cat /proc/<pid>/smaps | grep -E "(Size|Rss)" | awk '{sum+=$2} END {print sum " KB"}'
watch -n 1 'ps aux --sort=-%mem | head -10'
|
Valgrind 检测 C/C++内存泄漏
1 2 3 4 5 6 7
| gcc -g -o myapp myapp.c
valgrind --tool=memcheck --leak-check=full ./myapp
|
预防措施
代码审查要点
- 资源管理:确保每个 new/malloc 都有对应的 delete/free
- 智能指针:C++优先使用 shared_ptr/unique_ptr
- defer 语句:Go 中及时释放资源
- try-with-resources:Java 中自动管理资源
监控告警
1 2 3 4 5 6 7 8 9 10 11 12
| - alert: HighMemoryUsage expr: (process_resident_memory_bytes / 1024 / 1024) > 2000 for: 5m annotations: summary: "Process memory usage > 2GB"
- alert: MemoryGrowthRate expr: increase(process_resident_memory_bytes[1h]) / 1024 / 1024 > 500 for: 10m annotations: summary: "Memory growth rate > 500MB/hour"
|
内存泄漏问题往往隐蔽性强,影响面广。建立完善的监控和快速定位能力,是保障服务稳定性的关键。