当并非所有使用的堆都可以从线程访问时查找Java内存泄漏

我正在研究一个基于Java的大系统中潜在的内存泄漏(或至少是内存浪费)。 JVM的最大堆大小为5 GB,并且2-3GB堆使用量是应用程序的预期基线。 (可能有更高的峰值)

在我正在调查的重载场景中,堆被填满了。 使用“Eclipse MemoryAnalyzer Tool”分析堆转储显示(毫不奇怪)堆已完全用完。

MAT显示了2个潜在的泄漏候选者,两者大致保留了2.5GB:java.lang.Thread和来自系统的域对象,该对象在系统中的事务处理期间广泛使用。 但是,从Thread实例可以访问所有这些域对象(不足为奇)。 毕竟,这些线程正在处理事务。 因此,归因于java.lang.Thread的2.5 GB几乎完全是由那些域对象引起的。 这里不足为奇。

列出所有java.lang.Thread实例的对象树并总结所有线程的保留堆会导致2.5 GB的保留堆。

如果无法从java.lang.Thread的实例访问堆,那么我应该在哪里寻找填充堆所需的其他2.5 GB? – 终结器队列中没有任何内容 – 没有大量未到达的对象待处理GC

我认为另一种提出这个问题的方法是:“如何找到所有从java.lang.Thread实例无法访问的对象?可能是OQL查询?”,另一个问题是:“那里有什么样的对象无法从java.lang.Thread的实例到达终结器队列中的对象和待处理GC的未引用对象吗?“

我也遇到了我们网站内存泄漏的问题,
使用提供大量信息的yourkit java profiler ,凭借其能力,您可以拥有更广泛的图像,其中所有内存都被利用。
您可以使用上述工具找到一个很棒的教程Find Java Memory Leaks 。

你的问题,

“有什么样的对象是从java.lang.Thread的实例无法访问的,除了终结器队列中的对象和未引用的对象等待GC?”

有四种对象,

  1. 可以通过活动对象的引用直接到达的强对象
  2. 弱/软可达,具有弱/软参考的对象
  3. Pending Finalization,待完成的对象以及可通过终结器队列访问的对象
  4. 无法访问这些是从GC根无法访问但尚未收集的对象

除了这些JVM还使用本机内存,你可以在IBM Heap上找到它们的信息,JVM使用本机内存 , 感谢内存 ,根据YourKit, JVM内存结构有非堆内存,根据它们的定义是

此外,JVM具有堆以外的内存,称为非堆内存。 它在JVM启动时创建并存储每类结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,以及实习字符串。

由于额外的记忆没有在MAT中显示,因此很难知道建议什么。 如果有一些(甚至大部分)是你已经知道的事情我很抱歉,我只是想把我能想到的一切都拉到一起。

FindBugs的

FindBugs是一个静态分析工具,它将扫描您的代码,寻找常见的反模式和问题,并为您提供一个很好的报告。 它确实可以解决潜在内存和资源泄漏的许多原因。

手动转储

您可以尝试使用jmap或visualvm之类的东西来手动进行堆转储以进行分析,看看是否从让eclipse执行此操作得到了不同的结果:

http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jmap.html

http://java.dzone.com/articles/java-heap-dump-are-you-task

分析器怪癖

内存分析器FAQ:

http://wiki.eclipse.org/MemoryAnalyzer/FAQ

说:

症状:以交互方式监视内存使用情况时,使用的堆大小比MAT报告的大得多。

在创建索引期间,Memory Analyzer会删除无法访问的对象,因为各种垃圾收集器算法往往会留下一些垃圾(如果对象太小,移动并重新分配地址是昂贵的)。 但是,这应该不超过3%到4%。 如果您想知道删除了哪些对象,请按照此处的说明启用调试输出:MemoryAnalyzer / FAQ#Enable_Debug_Output

另一个原因可能是堆转储未正确写入。 如果通过jmap写入堆转储,则特别是较旧的VM(1.4,1.5)可能会出现问题。

启用调试输出将允许您查看那里发生的事情并确认该区域没有任何奇怪的内容。

其中一些提示可能是相关的

http://eclipsesource.com/blogs/2013/01/21/10-tips-for-using-the-eclipse-memory-analyzer/

使用JProfiler并按类分解堆对象数 – 查找哪个类有很多实例并在那里开始搜索。

您还可以在短时间内拍摄几个快照,并比较两个堆转储以查看在此期间创建的对象。 如果您知道某个操作导致了问题并且您想要忽略所有后台JVM对象噪声并且只是检查增量,那么这将特别方便。

我已经非常成功地使用它来发现内存泄漏。 它不是免费的,但它是值得的许可费。

仅供参考:我与JProfiler没有任何关系。

也许您应该在数据库连接器代码或ORM中查找内存泄漏。 因为如果在不关闭游标时使用原始连接库,则可能会导致内存泄漏。 我的第二个想法也与数据库连接器有关。 因为其中一些(可能不是你的)使用下面的本机代码,这是这个泄漏的来源。 由于大量的并发使用对我来说很敏感。 您可以根据需要进行检查。

Since the extra memory is not showing in MAT it's hard to know what to suggest. 事实并非如此。 MAT显示无法访问的对象。 只需转到de Preferences并选中启用此选项的复选框。 MAT重启后,您将看到这些对象的详细信息。 当然,GC的根将不可用。