在大型 Java 堆转储中查找内存泄漏的方法

新手上路,请多包涵

我必须在 Java 应用程序中查找内存泄漏。我对此有一些经验,但想就此的方法/策略提出建议。欢迎任何参考和建议。

关于我们的情况:

  1. 堆转储大于 1 GB
  2. 我们有 5 次堆转储。
  3. 我们没有任何测试用例来引发这种情况。它仅在使用至少一周后才会在(大规模)系统测试环境中发生。
  4. 该系统建立在一个内部开发的遗留框架之上,具有如此多的设计缺陷,以至于他们无法将它们全部计算在内。
  5. 没有人深入了解框架。它已被转移给印度的 一个 人,他几乎没有跟上回复电子邮件的步伐。
  6. 随着时间的推移,我们已经完成了快照堆转储,并得出结论,没有一个组件会随着时间的推移而增加。一切都在慢慢成长。
  7. 上面的内容给我们指出了一个方向,那就是框架自己开发的 ORM 系统无限制地增加了它的使用。 (这个系统将对象映射到文件?!所以不是真正的 ORM)

问题: 帮助您成功查找企业级应用程序中的漏洞的方法是什么?

原文由 Rickard von Essen 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 383
2 个回答

如果不了解底层代码,这几乎是不可能的。如果您了解底层代码,那么您可以更好地从堆转储中获得的大量信息中挑选出小麦和谷壳。

此外,如果一开始就不知道为什么要上课,就无法知道是否存在泄漏。

过去几周我就是这样做的,我使用了一个迭代过程。

首先,我发现堆分析器基本上没用。他们无法有效地分析巨大的堆。

相反,我几乎完全依赖 jmap 直方图。

我想你对这些很熟悉,但对于那些不熟悉的人:

 jmap -histo:live <pid> > histogram.out

创建活动堆的直方图。简而言之,它告诉您类名,以及每个类在堆中有多少个实例。

我每天 24 小时每 5 分钟定期倾倒堆。这对您来说可能过于细化,但要点是一样的。

我对这些数据进行了几种不同的分析。

我写了一个脚本来获取两个直方图,并找出它们之间的差异。因此,如果 java.lang.String 在第一次转储中为 10,在第二次转储中为 15,我的脚本将吐出“5 java.lang.String”,告诉我它上升了 5。如果下降了,数将为负。

然后,我将采用其中的几个差异,剔除从运行到运行下降的所有类,并对结果进行并集。最后,我会得到一个在特定时间跨度内不断增长的类列表。显然,这些是泄漏类的主要候选者。

但是,有些类保留了一些,而另一些类则进行了 GC。这些类总体上很容易上下波动,但仍然会泄漏。因此,他们可能会脱离“不断上升”的类别。

为了找到这些,我将数据转换为时间序列并将其加载到数据库中,特别是 Postgres。 Postgres 很方便,因为它提供了 统计聚合函数,因此您可以对数据进行简单的 线性回归分析,并找到趋势上升的类,即使它们并不总是位于图表的顶部。我使用了 regr_slope 函数,寻找具有正斜率的类。

我发现这个过程非常成功,而且非常高效。直方图文件不是特别大,而且很容易从主机上下载。它们在生产系统上运行的成本并不是特别高(它们会强制执行大型 GC,并且可能会暂时阻塞 VM)。我在具有 2G Java 堆的系统上运行它。

现在,这一切所能做的就是识别潜在的泄漏类。

这是理解如何使用这些类,以及它们是否应该成为它们发挥作用的地方。

例如,您可能会发现您有很多 Map.Entry 类或其他一些系统类。

除非您只是简单地缓存 String,否则这些系统类可能并不是“问题所在”,而这些系统类可能是“违规者”。如果您正在缓存某个应用程序类,那么该类可以更好地指示您的问题所在。如果您不缓存 com.app.yourbean,那么您将不会有关联的 Map.Entry 绑定到它。

有了一些类后,您就可以开始爬取代码库以查找实例和引用。由于您拥有自己的 ORM 层(无论好坏),您至少可以轻松地查看它的源代码。如果你的 ORM 正在缓存东西,它很可能会缓存包装你的应用程序类的 ORM 类。

最后,您可以做的另一件事是,一旦您了解了这些类,您就可以启动服务器的本地实例,使用更小的堆和更小的数据集,并使用一个分析器来对抗它。

在这种情况下,您可以进行仅影响您认为可能泄漏的 1 个(或少量)事物的单元测试。例如,您可以启动服务器,运行直方图,执行单个操作,然后再次运行直方图。您泄漏的班级应该增加 1(或者您的工作单元是什么)。

探查器也许能够帮助您跟踪那个“现已泄露”类的所有者。

但是,最后,您将不得不对您的代码库有一些了解,以便更好地理解什么是泄漏,什么不是,以及为什么一个对象存在于堆中,更不用说为什么它可能被保留作为堆中的泄漏。

原文由 Will Hartung 发布,翻译遵循 CC BY-SA 4.0 许可协议

查看 Eclipse 内存分析器。这是一个很棒的工具(并且是独立的,不需要安装 Eclipse 本身),它 1) 可以非常快速地打开非常大的堆,并且 2) 有一些非常好的自动检测工具。后者并不完美,但 EMA 提供了许多非常好的方法来浏览和查询转储中的对象以查找任何可能的泄漏。

我过去曾用它来帮助追查可疑的泄漏。

原文由 matt b 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题