Java String对象没有按时收集垃圾

Gee*_*eek 5 java garbage-collection memory-leaks memory-management

我有一个有趣的Java内存消耗问题.我有一个本机C++应用程序,它调用我的Java应用程序.

应用程序基本上做了一些语言翻译\解析一些XML并响应网络请求.大多数应用程序的状态不必保留,因此它充满了接受String参数并返回字符串结果的Methods.

随着时间的推移,这个应用程序继续占用越来越多的内存,并且有一段时间它开始占用接近2 GB的内存,这使我们怀疑某些Hashtable或静态变量中存在泄漏.经过仔细检查,我们没有发现任何泄漏.比较一段时间内的堆转储,显示char []和String对象占用大量内存.

然而,当我们检查这些char []时,我们发现它们没有GC根,这意味着它们不应该是泄漏的原因.由于它们是堆的一部分,这意味着它们正在等待垃圾收集.在使用了很好的工具MAT\VisualVM\JHat并滚动浏览了很多这样的对象后,我使用了yourkit的试用版.Yourkit直接提供数据,表示96%的char []和String无法访问.这意味着在进行转储时,堆中96%的字符串正在等待垃圾收集.

我知道GC运行很少但是当你通过VisualVM检查时,你实际上可以看到它正在运行:-(而不是如何在堆上有这么多未使用的对象.

IMO这个应用程序永远不应该超过400-500 MB内存,这是它在前24小时停留的地方,但它继续增加堆:-(

我正在运行Java 1.6.0-25.

请注意yourkit的截图

谢谢你的帮助.

Aar*_*lla 7

当你认为它/它应该是Java时,它不是GC :-) GC是一个太复杂的主题,无法花费几周的时间来理解正在发生的事情,真正深入研究细节.因此,如果你看到你无法解释的行为,那并不意味着它被打破了.

你看到的可能有几个原因:

  1. 您正在将一个巨大的String加载到内存中并保留对子字符串的引用.这可以将整个字符串保留在内存中(Java并不总是为子字符串分配新的char数组 - 因为字符串是不可变的,它只是重用原始的char数组并记住偏移量和长度).

  2. 到目前为止,没有任何事情触发GC.一些C++开发人员认为GC是"邪恶的"(任何你不理解的东西都必须是邪恶的,对吗?)因此他们将Java配置为不运行它,除非绝对必要.这意味着VM将占用内存,直到它达到最大值,然后,它将执行一次巨大的GC运行.

  3. build 25已经很老了.尝试更新到最新的Java版本(33,我认为).GC是VM中经过最佳测试的部分之一,但它确实存在错误.也许你打了一个.

  4. 除非您看到OutOfMemoryException,否则您没有泄漏.我们有一个应用程序可以吃掉你给它的所有堆.如果它有16GB的RAM("只是为了安全"),它将使用整个16GB,因为我们可以缓存.你永远不会看到内存不足,因为缓存会根据需要缩小,但系统管理员经常会惊慌失措 "哦天啊!天哪!它的内存耗尽 " PANIK不,它不是.除非Java告诉你,否则它不会耗尽内存.它只是有效地使用它.

  5. 使用命令行选项调整GC是打破它的最佳方法之一.数以百计的人比以往任何时候都更了解这个话题,他们将花费数年时间来提高GC效率.你认为你可以做得更好吗?祝好运.- >摆脱任何"魔术"命令行选项和调用System.gc(),你的问题可能会消失.

  • 亚伦:第4点正是发生的事情.这不是一个特别重要的组成部分,所以人们不高兴需要这么多的记忆.我想我将为它分配1 GB的最大堆空间,并将其配置为在内存不足时转储核心.如果没有泄漏,它实际上不应该出现内存不足.你还有其他建议吗? (2认同)