一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Java内存溢出问题解决:分析堆内存直方图(Histogram)

时间:2026-06-19 08:40:46 编辑:袖梨 来源:一聚教程网

直方图(Histogram)是MAT分析堆转储最常用入口,按类名统计实例数和内存占用,需重点关注业务类实例数异常偏高、增长失控现象,并结合Retained Heap与Path to GC Roots定位强引用源头。

直方图(Histogram)是MAT分析堆转储文件时最直观、最常用的入口视图,它按类名统计对象实例数量和总占用内存,能快速暴露“谁在吃内存”。关键不是看哪个类占得最多,而是看哪些业务类的实例数异常偏高、增长失控。

怎么看Histogram定位问题类

打开MAT → Load Heap Dump → 点击Histogram。默认按“Shallow Heap”排序,建议切换为按Objects列降序排列:

  • 重点关注你的自定义类(如Order、UserCache、ReportData),而不是String、byte[]这类基础类型
  • 如果某个业务类实例数达几十万甚至上百万,而正常场景下应只有几百或几千,基本就是泄漏源头
  • 对比“Shallow Heap”(对象自身大小)和“Retained Heap”(该对象被GC回收后能释放的总内存),Retained Heap高说明它牵连了一整片对象树

结合Retained Heap查引用链

在Histogram中右键点击可疑类 → 选择List objects → with incoming references,得到所有该类实例列表。再任选一个实例 → 右键 → Path to GC Roots → with all references

  • 这条路径会显示:这个对象为什么没被回收?它被谁强引用着?
  • 常见泄漏路径包括:static HashMapThreadLocal变量未关闭的监听器/回调线程池中的待执行任务
  • 若路径终点是java.lang.Thread或java.util.concurrent.ThreadPoolExecutor,说明对象被线程或线程池长期持有

过滤和分组技巧提升效率

直方图支持多种筛选方式,避免被系统类干扰:

立即学习“Java免费学习笔记(深入)”;

  • 在Histogram顶部输入框填com.yourpackage.,只显示你自己的包下类
  • 右键某类 → Group by → Package,把同类归并,便于发现某模块整体膨胀
  • 勾选右上角Use regular expressions,用.*Cache.*匹配所有含Cache的类名
  • 点击列头“Shallow Heap”可查看该类所有实例的内存分布直方图,识别是否存在个别超大实例

注意区分“多”和“大”

实例数量多 ≠ 一定泄漏;单个对象体积大 ≠ 一定有问题。要结合业务逻辑判断:

  • 一个byte[10485760](10MB)对象,如果是上传的大文件临时缓存,可能合理;如果是日志消息体且反复创建未清理,就危险
  • 10万个Order对象,若来自一次全量同步任务且任务结束后应清空,但实际一直存在,就是典型泄漏
  • 看到大量java.lang.ref.Finalizer,往往意味着有大量对象重写了finalize()且未及时被回收,需警惕

热门栏目