如何找到 Java 内存泄漏

如何在 Java 中发现内存泄漏(例如,使用 JHat) ?我尝试在 JHat 中加载堆转储,以便进行一个基本的查看。然而,我不明白我应该如何能够找到根引用(裁判)或任何它被称为。基本上,我可以说有几百兆字节的散列表条目([ java.util。HashMap $Entry 或者类似的东西) ,但是地图被广泛使用... ... 是否有某种方法可以搜索大型地图,或者可以找到大型对象树的一般根?

[编辑] 好吧,到目前为止我已经读了答案,但是我只能说我是一个便宜的混蛋(这意味着我对学习如何使用 JHat 比对支付 JProfiler 更感兴趣)。另外,JHat 总是可用的,因为它是 JDK 的一部分。当然,除非对于 JHat 来说,除了蛮力没有别的办法,但是我不相信会是这样。

此外,我不认为我将能够实际修改(添加 所有映射大小的日志记录) ,并运行它足够长的时间,以便我注意到泄漏。

153568 次浏览

当你修改地图的时候,总会有一个低技术含量的解决方案,那就是添加地图尺寸的日志,然后搜索那些地图尺寸超出合理范围的日志。

您确实需要使用跟踪分配的内存分析器。看看 JProfiler-它们的“堆行走”特性非常棒,而且它们集成了所有主要的 JavaIDE。它不是免费的,但也不是那么昂贵(单个许可证499美元)——你会花费500美元的时间,用不那么复杂的工具很快找到漏洞。

有一些工具可以帮助您找到泄漏,比如 JExplorer、 YourKit、 AD4J 或 JRockit 任务控制。最后一个是我个人最了解的。任何好的工具都应该允许您向下钻取到一个级别,在这个级别中,您可以轻松地识别哪些泄漏,以及分配哪些泄漏对象。

使用 HashTables、 Hashmap 或类似的方法是在 Java 中实际泄漏内存的少数几种方法之一。如果我必须手工找到泄漏,我会周期性地打印我的 HashMaps 的大小,然后从那里找到一个我添加项目并忘记删除它们的地方。

NetBeans 有一个内置的分析器。

一个工具是一个很大的帮助。

然而,有时候你不能使用一个工具: 堆转储是如此巨大,以至于工具崩溃了,你试图在一些生产环境中排除一台机器的故障,你只能访问 shell,等等。

在这种情况下,了解如何处理 hprof 转储文件将有所帮助。

寻找网站开始。这显示了哪些对象使用了最多的内存。但是这些对象并不是仅仅通过类型集中在一起的: 每个条目还包含一个“ trace”ID。然后,您可以搜索“ TRACE nnnn”以查看分配对象的堆栈顶部的几个帧。通常,一旦我看到对象被分配到哪里,我就会发现一个 bug,然后就完成了。另外,请注意,您可以使用-Xrunhprof 选项控制在堆栈中记录了多少帧。

如果您检查了分配站点,并且没有发现任何错误,那么必须开始从一些活动对象向根对象反向链接,以找到意外的引用链。这就是工具真正起作用的地方,但是您可以手工完成同样的事情(好吧,使用 grep)。不只有一个根对象(即不受垃圾收集约束的对象)。线程、类和堆栈帧充当根对象,它们强烈引用的任何内容都不可收集。

要进行链接,请在 HEAPDUMP 部分中查找具有错误跟踪 ID 的条目。这将带您到一个 OBJ 或 ARR 条目,它以十六进制显示一个唯一的对象标识符。搜索该 id 的所有匹配项,找到对象的强引用。在这些路径分支时向后跟踪它们,直到找到泄漏的位置。知道为什么工具这么方便了吧?

静态成员是内存泄漏的惯犯。事实上,即使没有工具,花几分钟查看静态 Map 成员的代码也是值得的。地图能变大吗?有什么东西能清理它的条目吗?

我使用以下方法来查找 Java 中的内存泄漏。我已经非常成功地使用了 jProfiler,但是我相信任何具有图形功能(差异更容易以图形形式进行分析)的专门工具都能够工作。

  1. 启动应用程序并等待它到达“稳定”状态,此时所有的初始化都已完成,应用程序处于空闲状态。
  2. 多次运行怀疑产生内存泄漏的操作,以允许进行任何缓存、 DB 相关的初始化。
  3. 运行 GC 并获取内存快照。
  4. 重新运行操作。根据操作的复杂性和所处理数据的大小,操作可能需要运行多次。
  5. 运行 GC 并获取内存快照。
  6. 对2个快照运行 diff 并分析它。

基本上,分析应该从最大的积极差异开始,比如,对象类型,并找出是什么原因导致这些额外的对象坚持在内存中。

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

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

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

由于人们建议使用多种工具(自从我在 JDK 和 JExplorer 试用版中得到可视化 wm 以来,我只尝试了这个工具) ,我认为我应该建议使用构建在 Eclipse 平台上的免费/开源工具,即 http://www.eclipse.org/mat/上的 Memory Analyzer (有时被称为 SAP 内存分析器)。

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

当您打开转储时,第一个屏幕显示一个饼图,其中包含最大的对象(计算保留的堆) ,并且可以快速导航到为了舒适而过大的对象。它也有一个发现可能泄漏的嫌疑人,我估计可以派上用场,但因为导航对我来说已经足够了,我没有真正进入它。

大多数时候,在企业应用程序中,给定的 Java 堆大于最大12到16GB 的理想大小。我发现很难让 NetBeans 分析器直接在这些大型 Java 应用程序上工作。

但通常这是不需要的。您可以使用 jdk 附带的 jmap 实用程序来获取“活动”堆转储,即 jmap 将在运行 GC 之后转储堆。对应用程序执行一些操作,等到操作完成后,再执行另一个“活动”堆转储。使用 Eclipse MAT 之类的工具来加载堆转储,对直方图进行排序,查看哪些对象增加了,哪些对象最高,这将提供线索。

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

这种方法只有一个问题: 巨大的堆转储(即使使用 live 选项)可能太大,无法转移到开发环节,并且可能需要一台具有足够内存/RAM 的机器来打开。

这就是类的直方图出现的地方。您可以使用 jmap 工具转储实时类直方图。这将只给出类的内存使用直方图。基本上它没有链接引用的信息。例如,它可以将 char 数组放在顶部。下面的字符串类。你得自己画出联系。

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

不采用两个堆转储,而是采用两个类直方图,如上所述; 然后比较类直方图并查看正在增加的类。看看是否可以将 Java 类与应用程序类关联起来。这会给出一个很好的提示。下面是一个 Python 脚本,它可以帮助您比较两个 jmap 直方图转储。Histogramparser.py

最后,像 JConolse 和 VisualVm 这样的工具对于查看内存随时间的增长以及查看是否存在内存泄漏是必不可少的。最后,有时您的问题可能不是内存泄漏,而是高内存使用。为此,可以启用 GC 日志记录; 使用更高级的、新的压缩 GC (如 G1GC) ; 还可以使用 jdk 工具(如 jstat)来实时查看 GC 行为

jstat -gccause pid <optional time interval>

其他对 google for-jhat,jmap,Full GC,Humongous distribution,G1GC 的引用

在多次调用垃圾收集器之后,您可以通过测量内存使用大小来找到答案:

Runtime runtime = Runtime.getRuntime();


while(true) {
...
if(System.currentTimeMillis() % 4000 == 0){
System.gc();
float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
System.out.println("Used memory: " + usage + "Mb");
}


}

如果输出数字相等,则应用程序中不存在内存泄漏,但如果看到内存使用数字之间的差异(数字增加) ,则项目中存在内存泄漏。例如:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

注意 ,有时需要一些时间来通过某些操作(如流和套接字)释放内存。你不应该根据第一次输出来判断,你应该在特定的时间内测试它。

使用 JProfiler 检查有关查找内存泄漏的 演员阵容。 这是对“迪马 · 马兰科的回答”的视觉解释。

注意: 虽然 JProfiler 不是免费的,但是试用版可以处理当前的情况。

由于我们大多数人已经使用 Eclipse 来编写代码,为什么不在 Eclipse 中使用内存分析器工具(Memory Analyser Tool,MAT)呢。效果很好。

Eclipse MAT是 Eclipse IDE 的一组插件,它提供了从 Java 应用程序分析 heap dumps和在应用程序中识别 memory problems的工具。

这有助于开发人员通过以下特性查找内存泄漏

  1. 获取内存快照(堆转储)
  2. 直方图
  3. 保留堆
  4. 支配者之树
  5. 探索 GC 根的路径
  6. 探长
  7. 公共内存反模式
  8. 对象查询语言

enter image description here

我最近处理了我们应用程序中的一个内存泄漏问题,在这里分享我的经验

垃圾收集器定期删除未引用的对象,但是它从不收集仍然被引用的对象。这就是可能发生内存泄漏的地方。

下面是一些查找引用对象的选项。

  1. 使用位于 JDK/bin文件夹中的 jvisualvm

选项: 查看堆空间 enter image description here

如果您看到堆空间不断增加,那么肯定存在内存泄漏。

要找出原因,可以在 sampler下使用 memory sampler

enter image description here

  1. 通过在应用程序的不同时间段使用 jmap(也可以在 JDK/bin文件夹中使用)获得 Java 堆直方图

    jmap -histo <pid> > histo1.txt
    

这里可以分析对象引用。如果一些对象从未被垃圾收集,那么这就是潜在的内存泄漏。

在本文中,您可以阅读导致内存泄漏的一些最常见原因: 了解 Java 中的内存泄漏