【问题标题】:OutOfMemoryException while working with String in Java在 Java 中使用 String 时出现 OutOfMemoryException
【发布时间】:2013-06-01 11:56:19
【问题描述】:

我有这样的情况:我从 CSV 文件中读取行并将它们放入List<String>。完成后,根据特殊逻辑对行进行解析,并将其部分作为key放入几个HashMap<String, Integer>中。然后列表记录被清除。其实我尝试了几种方法:

records.clear();
records = null;
records = new ArrayList<String>();

但似乎无论如何都没有释放内存(通过使用分析器和简单的打印到控制台检查它)。由于这种重复读取文件和进一步解析的迭代多次重复,有一次我得到一个OutOfMemoryError

有人可以在这里提出任何解决方案吗? Java有可能解决它吗?或者垃圾收集器的字符串池不可忽略?也许像 C++ 这样的其他语言更合适?

谢谢。

【问题讨论】:

  • 我们需要更多代码才能告诉您发生了什么...
  • 你清除了 hashmaps 吗?
  • 你有几百万行吗?
  • 您使用的是哪个 exact 版本的 Java?它可以产生很大的不同。文件有多大,你有多少内存?
  • 确保所有对你的字符串的引用都消失了。如果仍然可以从任何地方访问它们,则不会收集它们。如果你有大量的行,并且你将这些字符串存储在一大堆不同的 HashMap 中,那么你可能只是内存不足,简单明了。尝试确定(使用您的分析器)哪些对象持有对所有这些字符串(或其他东西!)的引用!

标签: java memory-management csv


【解决方案1】:

我们需要更多代码才能查看您是否在某处存在“内存泄漏”。

您是否考虑过在列表中存储更少的行而不是读取列表中的整个文件? 此外,您可以尝试完全取消中间结构。

  • 读取 100 行并将其添加到列表中
  • 遍历、解析并添加到哈希映射。
  • 清除列表

您可以增加堆大小,但如果您没有发现泄漏,如果您遇到非常大的文件大小,这可能会导致另一个异常。很好,dystroy 指出了这一点。

增加堆的说明位于: Increase heap size in Java

示例:java -Xmx6g myprogram

【讨论】:

  • 增加堆大小而不是寻找问题似乎是一种不好的反应。
  • 这是正确的!你提到的很好。我们需要来自 OP 的更多代码才能查看是否确实存在内存泄漏。然而,可能会通过增加堆来解决用户的问题。此外,也许一次读取较少的行(将问题分解为较小的行)也可以。
【解决方案2】:

java 中的 GC 运行良好。如果你得到OutOfMemoryError,你可能有内存泄漏(即你在你的集合中存储了太多)或者你没有为你的应用程序提供足够的堆。

我相信在您的情况下,您没有到达清除集合的代码。您可能在解析过程中失败。在这种情况下,首先尝试使用命令行选项-Xmx 为您的 java 进程添加更多内存,例如-Xmx1024M (1GB)。

我相信您将能够找到帮助您成功完成解析的选项。

然后,如果您正在使用仅解析文件一次并终止的实用程序,那么您就完成了。但是,如果您的应用程序应该运行并解析越来越多的文件,请检查处理每个文件后内存使用量是否没有增加。如果它正在增长,请检查它是设计导致的还是由错误引起的。

如果是设计,请考虑重新设计。顺便说一句,您真的必须将所有行读入内存然后处理它们吗?你在做什么处理?您是否有机会逐行处理文件并显着减少内存使用量?

【讨论】:

    【解决方案3】:

    你说:

    完成后,根据特殊逻辑对行进行解析,并将其部分作为键放入若干个HashMap中。

    如果您通过类似 String.substring 的方式获取这些部分,则该子字符串不是新副本,它实际上是指向原始字符串,并且知道包含子字符串的开始和索引。

    因此,只要这些子字符串中的任何一个存在,原始字符串就不会被垃圾收集。如果这些子字符串被传递到系统的其他部分,那么清除您的集合将无济于事。

    您需要确保创建了一个全新的字符串,例如:

    new String( myString.substring( 1, 5 ) );
    

    这是一个看起来不错的链接(谷歌搜索“字符串子字符串点在原始位置”)。 http://javarevisited.blogspot.com/2011/10/how-substring-in-java-works.html

    虽然显然后来的 JDK 1.7 版本已根据此修复了此问题: how the subString() function of string class works

    【讨论】:

      【解决方案4】:

      如果您使用records 列表中的行的子字符串填充这些哈希图,您实际上是为每个子字符串完全存储这些行。

      看看:Memory leak traps in the Java Standard API

      在这种情况下,答案是使用类似的东西:

      String key = new String(record.substring(6,12));
      

      String key = record.substring(6,12).intern();
      

      【讨论】:

        【解决方案5】:

        垃圾收集器仅在您释放对 Object 的所有引用时才起作用。你说HashMap中存储了一些信息,所以垃圾收集器不要删除它们。

        【讨论】:

          【解决方案6】:

          可能是你的内存够用,但是内存是零散的。如何构建 ArrayList 和 HashMap 至关重要。例如。正在使用StringBuilder

          除非显示出现错误的整个代码,否则很难远程调试内存问题。

          另外,如果我们知道 java 版本、环境等也会有所帮助。

          另外,不要忘记,如果你有很多不同大小的对象,内存会更容易碎片化。如果内存勉强足以容纳这些对象,则可能会出现内存错误。

          最后,您可以启动自己的垃圾收集(-而且很可能 JVM 会更清楚:-))。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-08-09
            • 2018-03-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多