你如何在Java中找到内存泄漏(例如,使用JHat)?我试图在JHat中加载堆转储以获得基本外观.但是,我不明白我应该如何能够找到根引用(ref)或其所谓的.基本上,我可以说有几百兆字节的哈希表条目([java.util.HashMap $ Entry或类似的东西),但地图遍布整个地方...有没有办法搜索大地图,或者可能找到大型对象树的一般根源?
[编辑]好的,到目前为止我已经阅读了答案,但我们只是说我是一个廉价的混蛋(这意味着我对学习如何使用JHat更感兴趣而不是支付JProfiler).此外,JHat始终可用,因为它是JDK的一部分.除非当然没有办法与JHat合作但是蛮力,但我无法相信可能是这样.
此外,我认为我不能实际修改(添加所有地图大小的记录)并运行它足够长的时间让我注意到泄漏.
我使用以下方法在Java中查找内存泄漏.我已经使用jProfiler取得了巨大的成功,但我相信任何具有图形功能的专业工具(差异更容易以图形形式分析)都可以.
启动应用程序并等待它进入"稳定"状态,此时所有初始化都已完成并且应用程序处于空闲状态.
运行怀疑产生内存泄漏的操作几次,以允许任何缓存,与DB相关的初始化发生.
运行GC并获取内存快照.
再次运行该操作.根据操作的复杂性和处理的数据大小,操作可能需要运行几次到多次.
运行GC并获取内存快照.
为2个快照运行差异并进行分析.
基本上,分析应该从最大的正面差异开始,比如对象类型,并找出导致这些额外对象粘在内存中的原因.
对于在多个线程中处理请求的Web应用程序,分析变得更加复杂,但仍然适用一般方法.
我做了很多专门用于减少应用程序内存占用的项目,这种一般方法通过一些特定于应用程序的调整和技巧总能很好地运行.
在这里发问者,我不得不说获得一个不需要5分钟来回答任何点击的工具,这样可以更容易地找到潜在的内存泄漏.
因为人们建议使用几种工具(我在JDK和JProbe试用版中只尝试了视觉效果)我虽然应该建议在Eclipse平台上构建一个免费/开源工具,内存分析器(有时称为SAP内存)分析器)可在http://www.eclipse.org/mat/上找到.
这个工具真正酷的是它在我第一次打开它时索引堆转储,这允许它显示像保留堆这样的数据而不需要等待每个对象5分钟(几乎所有操作都比我尝试的其他工具快得多) .
当您打开转储时,第一个屏幕会显示一个包含最大对象(计算保留堆)的饼图,并且可以快速向下导航到大而舒适的对象.它还有一个查找可能泄漏的嫌疑人,我reccon可以派上用场,但由于导航对我来说已经足够我没有真正进入它.
工具是一个很大的帮助.
但是,有时您无法使用工具:堆转储非常庞大而导致工具崩溃,您正试图在某些生产环境中对您只有shell访问权限的机器进行故障排除等.
在这种情况下,了解hprof转储文件的方法很有帮助.
寻找SITES BEGIN.这将向您显示哪些对象使用的内存最多.但是这些对象不是仅仅按类型集中在一起:每个条目还包括一个"跟踪"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直方图转储.histogramparser.py
最后,像JConolse和VisualVm这样的工具对于查看内存随时间的增长至关重要,并查看是否存在内存泄漏.最后,有时您的问题可能不是内存泄漏,而是内存使用率高.为此启用GC日志记录;使用更高级的新压缩GC,如G1GC; 你可以使用像jstat这样的jdk工具来实时查看GC行为
jstat -gccause pid
google for-jhat,jmap,Full GC,Humongous allocation,G1GC的其他参考
有一些工具可以帮助您找到泄漏,如JProbe,YourKit,AD4J或JRockit Mission Control.最后一个是我个人最熟悉的.任何好的工具都应该让您深入到可以轻松识别泄漏的位置以及泄漏对象的分配位置.
使用HashTables,Hashmaps或类似方法是您可以在Java中完全泄漏内存的几种方法之一.如果我不得不手工找到泄漏,我会周期性地打印我的HashMaps的大小,然后从那里找到我添加项目的那个并忘记删除它们.