先说说最基本的jstat,这玩意堪称内存监控的听诊器。命令行里敲个,就能每秒输出一次堆内存各区域使用率。盯着老年代(O)那个数字,要是每次Full GC后都不下降还稳步上升,基本可以锁定内存泄漏。关键是轻量,在生产环境几乎无感知。
但jstat只能告诉你发烧了,查不出病因。这时候就得请出jmap生成堆转储文件。执行,把整个堆内存快照抓下来。不过要注意,这个操作会触发Full GC,线上业务高峰期慎用。我一般先在预发环境抓取,实在不行再在业务低峰期操作。
拿到几十G的堆转储文件,就该MAT(Memory Analyzer Tool)大显身手了。这工具分析内存泄漏那叫一个专业。打开文件后直奔Leak Suspects报告,它会自动列出可能的内存泄漏点。有次我们系统泄漏,就是靠MAT发现某个缓存对象持有了一万多个业务对象,占用了近2G内存。
MAT里的直方图功能也特别实用,能按类统计对象数量和内存占用。按Retained Heap排序,一眼就能揪出内存消耗大户。有次发现String对象占用了不可思议的内存,顺藤摸瓜找到了日志组件配置错误导致的全量日志缓存问题。
对于线上环境,我越来越偏爱Arthas这个神器。不需要重启服务,直接attach上去就能诊断。命令实时查看内存变化,命令在线生成堆转储,甚至可以用命令监控方法调用频次。有次我们发现某个查询方法被疯狂调用,导致大量结果集对象堆积,就是靠Arthas实时定位的。
JProfiler这种图形化工具在开发阶段特别好用。它的内存录制功能可以实时显示对象创建和GC回收情况,看到哪些对象只增不减就特别可疑。CPU和内存的联合分析也很强大,能追溯到具体哪行代码分配了大量内存。
排查内存泄漏还有个技巧:在启动参数里加上,这样系统OOM时会自动生成堆转储文件。相当于给系统装了黑匣子,等出问题时直接分析就行。
实际排查中,内存泄漏常常出现在一些意想不到的地方:比如内部类持有外部类引用、ThreadLocal使用后没清理、静态集合持续添加元素、数据库连接未关闭等等。有次我们遇到Web容器线程池持有临时对象,每次请求都泄漏几百K,运行几天就把内存耗尽了。
工具再强大也只是手段,关键还是对代码保持敬畏。写完代码多想想:这个对象什么时候创建、什么时候销毁、会存活多久?养成定期代码审查的习惯,特别关注资源释放和集合使用。好的编程习惯比任何排查工具都管用。
内存泄漏排查就像破案,需要工具和经验的结合。建议大家在开发环境多演练整个排查流程,熟练使用这些工具,等真正出问题时才能从容应对。毕竟线上服务挂一小时,损失的可能就是真金白银啊。