如何找到Java内存泄漏

问题

你如何在Java中找到内存泄漏(例如,使用JHat)?我试图在JHat中加载堆转储以获得基本外观。但是,我不明白我应该如何找到根引用(ref)或其他任何名称。基本上,我可以说有几百兆字节的哈希表条目([java.util.HashMap $ Entry或类似的东西),但 Map 遍布整个地方...有没有办法搜索大 Map ,或者可能找到大型对象树的一般根源?

[编辑]好的,到目前为止我已经阅读了答案,但我们只是说我是一个廉价的混蛋(这意味着我更感兴趣的是学习如何使用JHat而不是支付JProfiler)。此外,JHat始终可用,因为它是JDK的一部分。除非当然没有办法与JHat相提并论蛮力,但我无法相信可能如此。

此外,我认为我不能实际修改(添加allmap大小的记录)并运行它足够长的时间让我注意到泄漏。


#1 热门回答(118 赞)

我使用以下方法在Java中查找内存泄漏。我已经使用jProfiler取得了巨大的成功,但我相信任何具有图形功能的专业工具(差异更容易以图形形式分析)都可以。

  • 启动应用程序并等待它进入"稳定"状态,此时所有初始化完成并且应用程序处于空闲状态。
  • 运行怀疑产生内存泄漏的操作几次,以允许任何缓存,与DB相关的初始化发生。
  • 运行GC并获取内存快照。
  • 再次运行操作。根据操作的复杂性和处理的数据大小,操作可能需要运行几次到多次。
  • 运行GC并获取内存快照。
  • 为2个快照运行差异并进行分析。

基本上,分析应该从最大的正面差异开始,比如对象类型,并找出导致这些额外对象粘在内存中的原因。

对于在多个线程中处理请求的Web应用程序,分析变得更加复杂,但仍然适用一般方法。

我做了很多项目,专门用于减少应用程序的内存占用,这种一般方法通过一些特定于应用程序的调整和技巧总是运行良好。


#2 热门回答(47 赞)

在这里发问者,我必须说一个工具不需要5分钟来回答任何点击,这样可以更容易地找到潜在的内存泄漏。

因为人们建议使用几种工具(我在JDK和JProbe试用版中只尝试了视觉效果)我虽然应该建议在Eclipse平台上构建一个免费/开源工具,内存分析器(有时称为SAP内存)分析仪)可在http://www.eclipse.org/mat/获得。

这个工具真正酷的是它在我第一次打开它时索引堆转储,这允许它显示像保留堆这样的数据,而不需要等待每个对象5分钟(几乎所有操作都比我尝试的其他工具快得多) 。

当你打开转储时,第一个屏幕会显示一个包含最大对象(计算保留堆)的饼图,并且可以快速向下导航到大而舒适的对象。它还有一个查找可能泄漏的嫌疑人,我reccon可以派上用场,但由于导航对我来说已经足够我没有真正进入它。


#3 热门回答(12 赞)

工具是一个很大的帮助。

但是,有时你无法使用工具:堆转储非常庞大,导致工具崩溃,你尝试对某些生产环境中的机器进行故障排除,而你只能访问shell等。

在这种情况下,了解hprof转储文件的方法很有帮助。

寻找SITES BEGIN。这将向你显示哪些对象使用的内存最多。但是对象不是仅仅按类型集中在一起:每个条目还包括一个"跟踪"ID。然后,你可以搜索"TRACE nnnn"以查看分配对象的堆栈的前几帧。通常,一旦我看到对象的分配位置,我就会发现一个错误而且我已经完成了。另请注意,你可以使用-Xrunhprof选项控制堆栈中记录的帧数。

如果你检查分配站点,并且没有看到任何错误,则必须从一些活动对象开始向后链接到根对象,以找到意外的引用链。这是一个工具真正有用的地方,但你可以手工做同样的事情(好吧,用grep)。不仅有一个根对象(即,不受垃圾收集的对象)。线程,类和堆栈框架充当根对象,它们强烈引用的任何东西都不可收集。

要进行链接,请在HEAP DUMP部分中查找具有错误跟踪ID的条目。这将带你进入OBJ或ARR条目,该条目以十六进制显示唯一的对象标识符。搜索该ID的所有匹配项,以查找谁有对该对象的强引用。在它们分支时向后跟随每条路径,直到找出泄漏的位置。了解为什么工具如此方便?

静态成员是内存泄漏的重复攻击者。实际上,即使没有工具,也值得花几分钟时间查看静态Map成员的代码。 Map 可以变大吗?有什么东西可以清理它的条目吗?