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



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

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

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

在大约 1000 个对象之后(取决于我打开了多少应用程序,可能是在 50 个对象之后(,我得到一个内存不足异常,并且使用 Process Explorer,我可以看到内存占用一直在增加,以对数步长为单位。

我尝试在将其设置为 null 后插入一个System.gc();,但内存使用量仍在不断增加,但现在不是使用对数步长,而是在每个处理对象后以大约 0.5Mb 步长。此外,在调试时,每当我单步跳过System.gc()占用空间时,占用空间都会增加此量,并且它保持不变,直到指令指针再次处于同一System.gc()

[编辑]

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

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

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

  • 尝试删除一些处理步骤(通过HTTP获取数据,解析HTML,提取数据(,并查看没有哪个步骤,内存增长将停止。此步骤会导致内存泄漏。

打电话给System.gc()也无济于事,永远。

提取子字符串时的一个问题可能是仍然引用了长原始字符串(如果要从一个原始字符串创建许多子字符串,则很好,如果原始字符串很长并且只想使用单个子字符串,则不好(。

尝试转储内存以查看保留了哪些对象以及它们被引用的位置。当内存已满时,可以使用 -XX:HeapDumpOnOutOfMemoryError 获取转储。你也可以使用 jmap -dump:format=b,file=heap.bin 来获取转储。有了这个,您可以在每次处理文档后获得转储,然后使用 Eclipse 内存分析器工具 (MAT( 比较转储以查看创建和保留了哪些新对象。

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

除了出于诊断目的外,很少有充分的理由显式调用垃圾回收器。

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

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

最新更新