【发布时间】:2010-12-17 16:27:29
【问题描述】:
我的应用程序加载了大约 1 个数据集。每次 85bm 到 100mb。应用程序的内存限制设置为 512mb,理论上这已经足够了。
但是,我发现如果在应用程序的单次运行中,我打开和关闭数据集 5 次,总内存消耗会稳步增加,直到出现内存不足错误:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6882 bguiz 20 0 679m 206m 19m S 30 13.7 0:30.22 java
6882 bguiz 20 0 679m 259m 19m S 9 17.2 0:55.53 java
6882 bguiz 20 0 679m 301m 19m S 9 20.0 1:20.04 java
6882 bguiz 20 0 679m 357m 19m S 33 23.7 1:44.74 java
6882 bguiz 20 0 679m 395m 19m S 80 26.2 2:10.31 java
内存从 ~14% 增长到 ~26%。看起来像是内存泄漏。
发生的事情是,正在加载的顶级数据用于填充地图和列表等集合,然后使用更详细的数据来创建这些顶级对象的子对象,然后将它们放入依次创建子子对象。
当数据集关闭时,当前应用程序确实会尝试通过取消填充各种对象集合来清除其踪迹,然后显式调用System.gc();
无论如何,这就是我开始申请时的状态(在我之前的几年里),我被分配了这个任务。
我需要做的,是想办法找出卸载数据集后,哪些子对象和子子对象还在相互引用,并纠正它们。
显然这可以手动完成,但会非常乏味,但我觉得通过内存分析来完成这项工作会是一个更好的选择,这是我以前没有做过的事情。
我阅读了其他一些关于使用哪些内存分析工具的 SO 问题,我选择使用 Netbeans IDE 中内置的工具,因为它似乎有很好的评论,而且我仍然在 Netbeans 工作。
之前是否有人做过类似的 Java 内存分析任务,事后看来:
- 您会给我什么具体建议?
- 您发现哪些技术有助于解决这个问题?
- 您发现哪些资源有助于解决此问题?
编辑: 此应用程序是标准桌面应用程序,而不是 Web 应用程序。
编辑:实施的解决方案
基本上对我有用的是将 Netbeans 的分析器与 JHAT 结合使用。
我发现 Netbeans IDE 中内置的 Profiler 在特定的分析点创建内存转储方面做得非常好,然后该工具能够按类过滤和排序并深入分析每个实例的参考。这一切都非常好。
但是,它没有为我提供比较两个堆转储的方法。我问了一个follow up question,看起来 JHAT(作为 JDK 的一部分)可以很好地完成这项工作。
Thorbjørn Ravn Andersen、Dmitry 和 Jason Gritman:你们的意见真的很有帮助,很遗憾我只能将 1 标记为正确答案,而且你们所有人都得到了我的 +1。
【问题讨论】:
-
您使用哪个 Java 版本?
-
当放置在离散对象中时,说 85mb 的数据会占用大约 85mb 的内存是不安全的。我们尝试将 4,000,000 条记录(每个记录由 5-10 个字段组成)加载到内存中,并在它们之间建立引用,并且内存使用量达到顶峰。
-
@Svante 我使用 Netbeans 6.7 在 JDK 1.6 上开发,目标平台是 Java 5 和 Java 6。@jt 是的,我知道,当我加载 85mb 数据集时,我预计内存使用量约为 100mb。但是,当卸载这 100mb 的大 sahre 时,并没有被取消引用(这就是问题所在)
-
很高兴听到您的想法。最终泄漏的源头是什么?
-
@Jason,泄漏有多个来源(但只有一个触发器),到目前为止,我已经解决了大约 15% 的泄漏,仅使用内存分析器 - 这些都相当明显:例如父对象已清除对其子对象的所有引用,但忘记清除它们的事件侦听器等。我才刚刚开始使用 JHAT 将一个堆与另一个堆进行比较。
标签: java memory-management memory-leaks garbage-collection profiling