浅堆是对象自身占用的内存,深堆是其能释放的全部内存;浅堆大暴露设计问题,深堆高且稳定指向内存泄漏;需结合支配树、GC Roots路径和多dump对比精准定位。

浅堆是对象本身在堆中占用的内存字节数,只算它自己的字段和对象头,不包含它引用的其他对象。深堆是这个对象被回收后,能一并释放的所有对象浅堆之和,也就是它“独占”的那部分内存空间。
浅堆帮你定位“胖对象”
浅堆大,说明这个对象自身结构臃肿,比如一个持有超大数组、超长字符串或大量基本类型字段的实例。它未必导致泄漏,但可能暴露设计问题或冗余数据存储。
常见情况包括:
- 缓存类中未做大小限制的 byte[] 或 char[] 字段
- DTO 对象里冗余携带了本不该序列化的二进制附件
- 日志上下文对象意外保留了整个 HTTP 请求体
深堆才是泄漏排查的黄金指标
内存泄漏的本质是“该死的对象没死”,而深堆直接反映一个对象实际把持了多少内存。深堆异常高,且其引用链长期稳定不收缩,就高度可疑。
关键操作建议:
- 打开支配树(Dominator Tree),按 Retained Heap 降序排列,排在最前面的几个节点就是内存占用主因
- 右键点击高深堆对象 → “Path to GC Roots” → 查看谁在强引用它;若路径中出现静态集合、单例缓存、线程局部变量等,就要重点检查生命周期管理
- 对比多次 dump:如果某对象的深堆持续增长,而它所属的业务逻辑本应周期性清理,基本可确认泄漏
两者结合才能避免误判
只看浅堆,容易把“大但合理”的对象当成问题;只看深堆,可能忽略间接泄漏源头。例如:
- 一个 Map 实例浅堆很小,但深堆极大——说明它装了大量值对象,真正的问题在 Map 的 put 逻辑是否失控
- 一个 ThreadLocal 变量浅堆不大,但其 value 的深堆飙升——泄漏根源不在 ThreadLocal 本身,而在它持有的业务对象未及时 remove
- ClassLoader 的深堆异常高——往往意味着整批类及其所有静态资源都没被卸载,需检查动态加载/热部署场景
物理差异背后是分析粒度的区别:浅堆是“个体体重”,深堆是“势力范围”。排查泄漏时,先靠深堆锁定嫌疑区域,再用浅堆和引用链穿透到具体字段和代码行。


















