内存泄漏定位需先用jstat观察old区持续上升及ygc频繁但o区不回落,再用jmap -histo:live查异常多实例或大对象类,最后通过heap dump的“paths to gc roots”锁定静态单例、未清理集合或未注销监听器等强引用持有者。

直接看大对象谁在“钉”着它不放——核心是顺着引用链回溯到 GC Roots,确认是不是单例、静态集合或未释放监听器在强持有。
先确认是不是真泄漏:看 GC 行为和内存趋势
别一上来就导 dump。先用基础命令快速判断:
-
jstat -gcutil
1000 5 :观察 Old 区(O 列)使用率是否持续上升、Full GC 后不回落;若 YGC 频繁但 O 区只增不减,大概率是老年代对象堆积 -
jmap -histo:live
:重点关注实例数异常多、或单个对象 size 很大的类,比如 byte[]、HashMap、ArrayList、自定义的Report或UserDetail等业务实体类 - 如果老年代占用高 + 某些类实例数线性增长,基本可锁定为泄漏候选
定位大对象的“根因持有者”:从 heap dump 查引用链
生成快照后,用 VisualVM、JProfiler 或 IDEA Profiler 打开:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 按“大小”或“实例数”排序,找到最占内存的那个类或数组
- 右键该对象 → “Paths to GC Roots” → 勾选 Exclude weak/soft references(排除弱软引用,聚焦强引用路径)
- 重点看引用链终点:是不是以
com.xxx.MySingleton、CacheManager、static final Map或ListenerHolder结尾?如果是,就找到了“钉子” - 特别注意静态字段(
private static xxx)、内部类隐式持外部类、未注销的回调等典型结构
检查三类高危静态持有结构
一旦定位到某个单例或工具类,立刻审查它的静态字段:
-
静态集合没清理:如
private static List<bigdata> buffer = new ArrayList();</bigdata>—— 添加后从不 remove,也不设上限 -
缓存无淘汰机制:如
private static Map<string byte> rawCache = new HashMap();</string>—— key 不唯一、不设过期、不控制 size -
持有短命对象:如单例里存了
HttpServletRequest、Activity(Android)、或某个 Controller 实例 —— 这些对象本该随请求/页面结束而销毁,却被长生命周期单例锁死
验证与修复要同步做
改代码前,先加日志或监控验证假设:
- 在疑似单例的 add 方法里打点,统计缓存 size 变化;或暴露 JMX 属性实时查看 map.size()
- 修复方向明确:强引用 → 改
WeakReference/SoftReference;原始集合 → 换Caffeine或ConcurrentHashMap+ 定时清理;必须持有时 → 加clearCache()入口并在配置刷新、模块卸载时调用 - 上线后继续用
jstat对比 Old 区走势,确认增长趋势被遏制
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










