繁体   English   中英

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

[英]Java String objects not getting garbage collected on time

我有一个有趣的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的截图

谢谢你的帮助。

当你认为它/它应该是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() ,你的问题可能会消失。

尝试将堆大小减小到500兆字节,看看软件是否会开始垃圾收集或死亡。 Java对于使用给它的内存来说并不是太挑剔。 您也可以研究GC调整选项,这将使GC更清楚地清理东西。

String reallyLongString = "this is a really long String";
String tinyString = reallyLongString.substring(2, 3);
reallyLongString = null

在上面的例子中,JVM无法收集为长字符串分配的内存,因为它有部分引用。 如果你正在使用Strings,你会遇到记忆问题,这可能是你悲伤的原因。

使用tinyString = new String(reallyLongString.substring(2, 3);相反。

可能根本没有泄漏 - 如果String可以访问则会发生泄漏。 如果你已经为应用程序分配了多达2GB的内容,那么垃圾收集器就没有理由在你接近该限制之前开始释放内存。 如果您不希望它占用超过500MB,则在启动JVM时传递-Xmx 512m

您也可以尝试调整垃圾收集器以便更早地开始清理。

首先,不要再担心那些Strings和char []了。 在我所描述的几乎所有Java应用程序中,它们都位于内存使用者列表的顶部。 几乎没有那些Java应用程序,他们是真正的问题。

如果你还没有收到OutOfMemoryError,但是担心2GB对你的java进程来说太多了,那么试着减少传递给它的Xmx值。 如果运行良好且512m或1g好,那问题就解决了,不是吗?

如果你得到OOM,那么你可以尝试的另一个选择是在你的java进程中使用Plumbr 它是内存泄漏发现工具,如果确实存在内存泄漏,它可以帮助您。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM