Java Memory Leak - 查找类Y的所有对象,而不是类Y引用的

时间:2014-02-10 17:16:32

标签: java memory-leaks visualvm

我在Java中有一个内存泄漏,我的堆转储中有9600 ImapClient个,只有7800 MonitoringTask个。这是一个问题,因为每个ImapClient都应归MonitoringTask所有,因此额外的1800 ImapClient会被泄露。

一个问题是我无法在堆转储中隔离它们,看看是什么让它们保持活着。到目前为止,我只能通过使用外部证据来猜测哪些ImapClient正悬空。我正在学习OQL,我相信它可以解决这个问题,但它会慢慢来,而且我需要一段时间才能理解如何以新的查询语言执行这样的递归操作。

确定存在泄漏是困难的,所以这是我的全部情况:

  • 这个过程在一周前喷出了OOME。我以为我修好了它,我试图验证我的固定工作是否能够在没有等待另一整周的情况下工作,看它是否会再次喷出OOME。
  • 此任务在启动时创建7000-9000 ImapClient,然后在正常操作下连接并断开其中的极少数。
  • 我检查了另一个运行较早的OOME前代码的进程,它显示的数字为9000/9100而不是7800/9600。我不知道为什么旧代码会与新代码不同,但这是泄漏的证据。

这个问题的关键在于我可以确定是否存在泄漏。每个ImapClient都应该是MonitoringTask的裁判,这是一项业务规则。如果我询问的这个查询是空的,则没有泄漏。如果它提出了对象,再加上这个商业规则,它不仅是泄漏的证据,而且是一个证据。

3 个答案:

答案 0 :(得分:3)

您的期望不正确,没有发生任何泄漏的实际证据

  

垃圾收集器的目标是在需要时释放空间   只有这样,其他任何事情都是浪费资源。绝对是   试图保留尽可能多的自由空间没有任何好处   一直可用,只有不同的。

     

仅仅因为某些东西是垃圾收集的候选者没有   意味着它将被实际收集,并且没有办法   强制垃圾收集。

我在任何地方都没有看到OutOfMemoryError

你关心的是你无法控制,而不是直接控制

您应该关注的是控制中的内容,即确保您不会持续超过您需要的参考,并且您不会不必要地复制内容。 Java中的垃圾收集例程已经过高度优化,如果您了解其算法的工作原理,您可以确保您的程序以最佳方式运行,以使这些算法正常工作。

Java堆内存与其他语言的手动内存不同,这些规则不适用

在其他语言中被认为是内存泄漏与Java及其垃圾收集系统不同,它们与根本原因不同。

Java内存中很可能不会被泄漏的单个超级对象(在其他环境中悬挂引用)消耗掉。

由于垃圾收集器所处的范围以及许多其他可能在运行时发生变化的事情,中间对象可能会被垃圾收集器保持的时间超过预期。

示例:垃圾收集器可能会认为有候选者,但是因为它认为仍有大量内存可能需要花费太多时间才能将其清除掉时间点,它会等到记忆压力升高。

垃圾收集器现在真的很好,但它不是魔术,如果你正在做退化的事情,它将导致它不能最佳地工作。互联网上有很多关于所有JVM版本的垃圾收集器设置的文档。

这些未引用的对象可能还没有达到垃圾收集器认为需要它们从内存中清除它们的时间,或者可能有一些其他对象(List对它们进行引用。 )例如,你没有意识到仍然指向该对象。这就是Java中最常被称为 leak 的东西,它更具体地是一个参考泄漏。

我没有看到提及OutOfMemoryError

您的代码中可能没有问题,垃圾收集系统可能没有足够的压力来启动并释放您认为它应该清理的对象。 您认为是一个问题可能不是,除非您的程序与OutOfMemoryError崩溃。这不是C,C ++,Objective-C或任何其他手动内存管理语言/运行时。你没有在你期望的细节层面上决定你的内存是什么。

答案 1 :(得分:0)

检查代码中的终结器,尤其是与IMapclient相关的任何内容。

可能是您的MonitoringTasks很容易被收集,而您的IMapclient已经完成,因此留在堆上(虽然已经死了),直到终结器线程运行。

答案 2 :(得分:-1)

显而易见的答案是在代码中添加WeakHashMap<X, Object>(和Y) - 一个跟踪X的所有实例,另一个跟踪Y的所有实例(使它们成为类的静态成员和在构造函数中将每个对象插入到映射中,并使用null'value')。然后,您可以随时迭代这些地图以查找X和Y的所有实时实例,并查看哪些X未被Y引用。您可能希望首先触发完整的GC,以忽略已死且尚未收集的对象。