Java字符串对象未及时收集垃圾

Java字符串对象未及时收集垃圾,java,memory-management,memory-leaks,garbage-collection,Java,Memory Management,Memory Leaks,Garbage Collection,我有一个关于Java内存消耗的有趣问题。我有一个本地的C++应用程序,它调用我的java应用程序。 该应用程序基本上执行一些语言翻译\解析一些XML并响应网络请求。应用程序的大部分状态不必保留,因此它充满了接受字符串参数并返回字符串结果的方法 随着时间的推移,这个应用程序继续占用越来越多的内存,并且有一段时间它开始占用接近2GB的内存,这让我们怀疑某些哈希表或静态变量中的某个地方存在泄漏。经过仔细检查,我们没有发现任何泄漏。比较一段时间内的堆转储,可以看出char[]和String对象占用了大量

我有一个关于Java内存消耗的有趣问题。我有一个本地的C++应用程序,它调用我的java应用程序。 该应用程序基本上执行一些语言翻译\解析一些XML并响应网络请求。应用程序的大部分状态不必保留,因此它充满了接受字符串参数并返回字符串结果的方法

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

然而,当我们检查这些char[]字符串时,我们发现它们没有GC根,这意味着它们不应该是泄漏的原因。由于它们是堆的一部分,这意味着它们正在等待垃圾被收集。在使用了各种工具MAT\VisualVM\JHat并滚动浏览了大量此类对象之后,我使用了yourkit的试用版。Yourkit直接给出数据,表示96%的char[]和String是不可访问的。这意味着在获取转储时,堆中96%的字符串正在等待垃圾回收

我知道GC的运行很节省,但是当你通过VisualVM检查时,你可以看到它在运行:-(为什么堆上总是有这么多未使用的对象呢

IMO此应用程序占用的内存不应超过400-500 MB,这是它在前24小时停留的位置,但会继续增加堆:-(

我正在运行Java1.6.0-25


感谢您的帮助。

尝试将堆大小减小到500 MB,看看软件是否会开始垃圾收集或消亡。Java对使用提供给它的内存并不太挑剔。您还可以研究GC调优选项,这将使GC在清理内容时更加谨慎

String reallyLongString = "this is a really long String";
String tinyString = reallyLongString.substring(2, 3);
reallyLongString = null
在上述情况下,JVM无法收集为长字符串分配的内存,因为有一个对它的一部分的引用。 如果你在用字符串做事情,而你正遭受记忆问题的折磨,这可能是你悲伤的原因


使用
tinyString=newstring(reallyLongString.substring(2,3);
取而代之。

Java在你认为它是/应该是时不使用GC:-)GC是一个太复杂的主题,如果不花几周的时间真正深入研究细节,就无法理解正在发生的事情。因此,如果你看到无法解释的行为,这并不意味着它已经崩溃

你所看到的有几个原因:

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

  • 有些C++开发者认为GC是“邪恶的”(任何你不理解的东西都是邪恶的,对吧?)所以他们配置java,除非绝对必要,否则不运行它。这意味着VM会在达到最大值之前消耗内存,然后,它会做一个巨大的GC运行。

  • build 25已经很旧了。请尝试更新到最新的Java build(我认为是33)。GC是VM中测试最好的部分之一,但它确实存在bug。也许你遇到了bug

  • 除非你看到OutOfMemoryException,否则你就不会有漏洞。我们有一个应用程序,它会吃掉你给它的所有堆。如果它得到16GB的RAM(“为了安全起见”),它将使用整个16GB,因为我们缓存我们能缓存的。你永远不会看到内存不足,因为缓存会根据需要收缩,但系统管理员经常会发疯“哦,天哪!哦,天哪!它的内存不足”PANIK不,不是。除非Java告诉你,否则它并没有内存不足。它只是在高效地使用它

  • 使用命令行选项优化GC是打破它的最佳方法之一。数百名对该主题了解得比您更多的人将花费数年时间使GC更高效。您认为您可以做得更好吗?祝您好运。->摆脱任何“神奇”的命令行选项和对
    System.GC()的调用
    您的问题可能会消失


  • 可能根本不存在泄漏-如果
    字符串
    是可访问的,则可能存在泄漏。如果您为应用程序分配了多达2GB的内存,则在接近该限制之前,垃圾收集器没有理由开始释放内存。如果您不希望占用的内存超过500MB,则在以下情况下传递
    -Xmx 512m
    启动JVM


    您还可以尝试更早地开始清理。

    首先,不要再担心那些字符串和字符[]。在我分析过的几乎所有java应用程序中,它们都位于内存消耗列表的顶部。在这些java应用程序中,几乎没有一个是真正的问题

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


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

    您正在运行什么JVM?您的JVM运行时参数是什么?尝试使用
    -verbose:gc
    JVM标志运行应用程序,并粘贴所有额外的输出。Sky,I更新了问题本身中的JVM。我们只需在调用JVM时传递Xms,而不传递任何其他内容。如果它实际持有引用,则您会将其视为指向GC根的路径。此外,substring()方法已经复制了字符串。@StevenSchlansker:
    string.substring()
    不会复制任何内容。它重用