内存增长无限,垃圾收集器增加了占用空间

时间:2012-05-13 16:35:53

标签: java

所以我有一个创建类似2000个对象的应用程序。

对于每个对象,它下载一个网页(大约75kb的字符串),创建整个html树的DOM文档对象模型并丢弃字符串(它超出范围)。

然后从DOM中提取一些文本和链接,并丢弃DOM(通过将其设置为null)。

在大约1000个对象之后(取决于我打开了多少应用程序,它可能在50个对象之后)我得到一个OutOfMemory异常,并且使用Process Explorer我可以看到内存占用量一直在以对数步长增加。 / p>

我尝试在将System.gc();设置为null后插入System.gc(),但内存使用量仍在不断增加,但现在不是采用对数步骤,而是在每个已处理对象后步长约为0.5Mb。此外,在调试时,每当我跳过System.gc()时,脚印增加了这个量,并且它会保持不变,直到指令指针再次处于同一{{1}}。

[编辑]

我按照答案中的建议在转储上运行配置文件,发现每个类仍然存储一个150kb的字符串(75k字符)。总计242mb。所以问题就变成了,如何在不保留原始字符串的情况下保留子字符串?显然,String构造函数就是这样做的。

4 个答案:

答案 0 :(得分:2)

这看起来像是内存泄漏。我猜你在解析HTML(?)后没有关闭HTTP连接或清理,但它只是在猜测。您有两种方法可以诊断问题:

  • 在内存不足错误(-XX:+HeapDumpOnOutOfMemoryError)上转储内存并使用内存分析器。它会告诉你什么占据了大部分内存

  • 尝试删除一些处理步骤(通过HTTP获取数据,解析HTML,提取数据),然后看看内存增长停止的步骤。此步骤导致内存泄漏。

同样致电System.gc()也无济于事。

答案 1 :(得分:1)

首先,您不能强制JVM进行垃圾收集。您只能提出建议API。进一步将某些内容设置为null并不能保证已删除对该对象的所有引用。我的猜测是你忘记了String pool没有看到任何代码,这些是我们必须要做的假设。此外,您应该查看缓存结果,而不是每次丢弃它们,因为它是JVM中的时间和资源的巨大浪费。

答案 2 :(得分:1)

一个问题可能是在提取子字符串时仍然引用了长原始字符串(如果你想从一个原始字符串中创建很多子字符串,那么这个问题很好,如果原始字符串非常长并且你只想使用单个子字符串,则会很糟糕)。

尝试转储内存以查看保留哪些对象以及引用它们的位置。当内存已满时,可以使用 -XX:HeapDumpOnOutOfMemoryError 获取转储。您还可以使用 jmap -dump:format = b,file = heap.bin 来获取转储。通过这种方式,您可以在每次处理文档后获取转储,然后使用Eclipse Memory Analyzer Tool(MAT)比较转储,以查看创建和保留的新对象。

答案 3 :(得分:0)

除了诊断目的外,很少有理由明确调用垃圾收集器。

当您从DOM中提取字符串时,请确保实际上(或)实现自己的对象池,如果程序的另一部分保留对直接来自DOM的任何内容的引用。

使用您的探查器确认没有其他内容保留对DOM或您认为丢弃的其他对象的引用。还要记住,Java的内置DOM实现可能有大约5倍的内存开销,并确保您的最大堆大小(-Xmx)足够大。