[英]How to find a Java Memory Leak
您如何发现 Java 中的内存泄漏(例如,使用 JHat)? 我尝试在 JHat 中加载堆转储以进行基本查看。 但是,我不明白我应该如何找到根引用( ref )或它的名字。 基本上,我可以说有几百兆字节的哈希表条目([java.util.HashMap$Entry 或类似的东西),但是到处都在使用地图......有没有办法搜索大地图,或者可能找到大对象树的一般根?
[编辑] 好的,到目前为止我已经阅读了答案,但让我们说我是一个廉价的混蛋(这意味着我对学习如何使用 JHat 比为 JProfiler 付费更感兴趣)。 此外,JHat 始终可用,因为它是 JDK 的一部分。 除非当然 JHat 没有办法只能使用蛮力,但我不敢相信情况会如此。
此外,我认为我无法实际修改(添加所有地图大小的日志记录)并运行它足够长的时间让我注意到泄漏。
我使用以下方法来查找 Java 中的内存泄漏。 我已经使用 jProfiler 取得了巨大的成功,但我相信任何具有图形功能(差异更容易以图形形式分析)的专业工具都可以使用。
基本上分析应该从最大的正差异开始,比如对象类型,并找出导致这些额外对象留在内存中的原因。
对于在多个线程中处理请求的 Web 应用程序,分析变得更加复杂,但通用方法仍然适用。
我做了很多专门旨在减少应用程序内存占用的项目,这种带有一些特定于应用程序的调整和技巧的通用方法总是很有效。
发问者在这里,我不得不说获得一个不需要 5 分钟来回答任何点击的工具可以更容易地找到潜在的内存泄漏。
由于人们建议使用几种工具(自从我在 JDK 和 JProbe 试用版中得到它以来,我只尝试了 Visual wm)我虽然我应该建议一个构建在 Eclipse 平台上的免费/开源工具,内存分析器(有时被称为 SAP 内存分析器)在http://www.eclipse.org/mat/ 上可用。
这个工具真正酷的是,当我第一次打开它时它索引了堆转储,这使得它可以显示保留堆之类的数据,而无需为每个对象等待 5 分钟(几乎所有操作都比我尝试过的其他工具快很多) .
当您打开转储时,第一个屏幕会向您显示一个饼图,其中包含最大的对象(计算保留的堆),您可以快速向下导航到为了舒适而大的对象。 它还有一个 Find 可能的泄漏嫌疑人,我认为它可以派上用场,但由于导航对我来说已经足够了,我并没有真正深入了解它。
一个工具是一个很大的帮助。
但是,有时您无法使用工具:堆转储如此之大以致于使工具崩溃,您正在尝试对某些生产环境中的机器进行故障排除,而您只能访问该工具,等等。
在这种情况下,了解 hprof 转储文件的方法会很有帮助。
寻找网站开始。 这会显示哪些对象使用的内存最多。 但是对象并不仅仅按类型混在一起:每个条目还包括一个“跟踪”ID。 然后,您可以搜索该“TRACE nnnn”以查看分配对象的堆栈的前几帧。 通常,一旦我看到对象的分配位置,我就会发现一个错误,然后我就完成了。 另请注意,您可以使用 -Xrunhprof 选项控制堆栈中记录的帧数。
如果您查看分配站点,并且没有发现任何错误,则必须开始从其中一些活动对象到根对象的反向链接,以找到意外的引用链。 这是工具真正有用的地方,但您可以手动执行相同的操作(好吧,使用 grep)。 不只有一个根对象(即,不受垃圾回收影响的对象)。 线程、类和堆栈帧充当根对象,并且它们强烈引用的任何内容都不可收集。
要进行链接,请在 HEAP DUMP 部分查找具有错误跟踪 ID 的条目。 这将带您到一个 OBJ 或 ARR 条目,它以十六进制显示唯一的对象标识符。 搜索所有出现的该 id 以找到谁对该对象有强引用。 沿着每条路径向后走,直到您找出泄漏的位置。 看看为什么一个工具如此方便?
静态成员是内存泄漏的重犯。 事实上,即使没有工具,花几分钟查看静态 Map 成员的代码也是值得的。 地图可以变大吗? 有什么东西可以清理它的条目吗?
大多数情况下,在企业应用程序中,给定的 Java 堆大于最大 12 到 16 GB 的理想大小。 我发现很难让 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 数组放在顶部。 和下面某处的 String 类。 您必须自己绘制连接。
jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt
不是采用两个堆转储,而是采用两个类直方图,如上所述; 然后比较类直方图,看看增加的类。 看看您是否可以将 Java 类与您的应用程序类相关联。 这将给出一个很好的提示。 这是一个 pythons 脚本,可以帮助您比较两个 jmap 直方图转储。 直方图解析器.py
最后,像 JConolse 和 VisualVm 这样的工具对于查看内存随时间的增长以及是否存在内存泄漏至关重要。 最后,有时您的问题可能不是内存泄漏,而是内存使用率过高。为此启用 GC 日志记录;使用更先进的新压缩 GC,如 G1GC; 您可以使用 jdk 工具(如 jstat)实时查看 GC 行为
jstat -gccause pid <optional time interval>
google for -jhat、jmap、Full GC、Humongous allocation、G1GC 的其他参考资料
有一些工具可以帮助您找到漏洞,例如 JProbe、YourKit、AD4J 或 JRockit Mission Control。 最后一个是我个人最了解的。 任何好的工具都应该让您深入到可以轻松识别泄漏的级别以及泄漏对象的分配位置。
使用 HashTables、Hashmaps 或类似的方法是在 Java 中完全泄漏内存的少数几种方法之一。 如果我必须手动找到泄漏,我会定期打印我的 HashMap 的大小,然后从那里找到我添加项目并忘记删除它们的那个。
好吧,总是有一种低技术的解决方案,即在修改地图时添加地图大小的日志记录,然后搜索地图增长超出合理大小的日志。
NetBeans 有一个内置的分析器。
您确实需要使用跟踪分配的内存分析器。 看一看JProfiler - 他们的“heap walker”特性很棒,并且它们与所有主要的 Java IDE 集成。 它不是免费的,但也不是那么昂贵(单个许可证 499 美元) - 您将很快消耗价值 500 美元的时间,努力使用不太复杂的工具寻找泄漏。
可以通过多次调用垃圾收集器后测量内存使用大小来找出:
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 查找内存泄漏的屏幕截图。 这是@Dima Malenko Answer 的视觉解释。
注意:虽然 JProfiler 不是免费软件,但试用版可以应对当前的情况。
我最近处理了我们应用程序中的内存泄漏。 在这里分享我的经验
垃圾收集器会定期删除未引用的对象,但从不收集仍在被引用的对象。 这是可能发生内存泄漏的地方。
以下是查找引用对象的一些选项。
JDK/bin
文件夹中的jvisualvm
如果您看到堆空间不断增加,则肯定存在内存泄漏。
要找出原因,您可以使用memory sampler
下的memory sampler
sampler
。
在应用程序的不同时间跨度使用jmap
(在JDK/bin
文件夹中也有)获取Java堆直方图
jmap -histo <pid> > histo1.txt
这里可以分析对象引用。 如果某些对象从未被垃圾回收,那就是潜在的内存泄漏。
您可以在本文中阅读一些最常见的内存泄漏原因:了解 Java 中的内存泄漏
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.