1

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

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

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

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

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

[编辑]

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

4

4 回答 4

2

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

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

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

打电话System.gc()也帮不了你,永远。

于 2012-05-13T16:43:13.177 回答
1

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

于 2012-05-13T16:42:03.320 回答
1

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

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

于 2012-05-14T06:53:59.260 回答
0

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

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

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

于 2012-05-14T07:38:44.823 回答