为什么我的hashset如此耗费内存?

时间:2011-11-07 14:58:39

标签: java hashset

我发现我的程序正在增加的内存是因为下面的代码,目前我正在读一个大约7GB的文件,我相信将存储在hashset中的文件比10M还要多,但是内存我的程序不断增加到300MB然后由于OutofMemoryError而崩溃。如果是Hashset问题,我应该选择哪种数据结构?

    if(tagsStr!=null) {
        if(tagsStr.contains("a")||tagsStr.contains("b")||tagsStr.contains("c")) {
            maTable.add(postId);
        }
    } else {
        if(maTable.contains(parentId)) {
            //do sth else, no memories added here
        }
    }

4 个答案:

答案 0 :(得分:7)

你还没有真正告诉我们你在做什么,但是:

  • 如果你的文件当前是ASCII格式的,你读取的每个字符都是文件中的一个字节或内存中的两个字节。
  • 每个字符串都有一个对象开销 - 如果你要存储大量的小字符串,这可能很重要
  • 如果您正在阅读BufferedReader行(或从大字符串中获取子字符串),则每个行都可能有一个大的后备缓冲区 - 您可能希望使用maTable.add(new String(postId))来避免这种情况
  • 散列集中的每个条目都需要一个单独的对象来保存key / hashcode / value / next-entry值。同样,有很多条目,这可以加起来

简而言之,你很可能没有做错任何事情,但是内存增加因素的组合对你不利。其中大部分是不可避免的,但第三个可能是相关的。

答案 1 :(得分:3)

您遇到了内存泄漏,或者您对存储的字符串数据量的理解不正确。如果没有看到更多的代码,我们无法分辨出来。

科学的解决方案是使用内存分析器运行您的应用程序,并分析输出以查看哪些数据结构正在使用意外的大量内存。


如果我猜测,那就是你的应用程序(在某种程度上)正在做这样的事情:

String line;
while ((line = br.readLine()) != null) {
    // search for tag in line
    String tagStr = line.substring(pos1, pos2);
    // code as per your example
}

这比你期望的使用更多的内存。 substring(...)调用会创建一个tagStr对象,该对象引用原始line字符串的后备数组。您希望缩短实际的标记字符串是指包含原始行中所有字符的char[]对象。

修复方法是这样做:

    String tagStr = new String(line.substring(pos1, pos2));

这将创建一个String对象,该对象不共享参数String的支持数组。

更新 - 根据您的最新数据,此类或类似内容的解释越来越多......


为了扩展Jon Skeet的另一个观点,一个小字符串的开销非常高。例如,在典型的32位JVM上,单字符串的内存使用情况为:

  • String对象的字符串对象标题:2个字
  • 字符串对象字段:3个字
  • 填充:1个字(我认为)
  • 支持数组对象标题:3个字
  • 支持数组数据:1个字

总计:10个字--40个字节 - 如果您的输入是8位字符集,则可容纳一个char个数据...或一个byte个数据。

(这不足以解释你的问题,但无论如何你应该知道它。)

答案 2 :(得分:0)

无法将某些读入内存的数据(来自7G文件)以某种方式释放?艾克乔恩提出的东西......即。因为字符串是不可变的,所以每次读取字符串都需要创建一个新的String对象,如果GC不够快,可能会导致内存不足......

如果是上述情况,则可能会在代码/迭代中插入一些“断点”,即。在某些已定义的点上,发出gc并等到它终止。

答案 3 :(得分:0)

使用 -XX:+ HeapDumpOnOutOfMemoryError 运行程序。然后,您就可以使用像MAT这样的内存分析器来查看正在耗尽所有内存的内容 - 这可能是完全出乎意料的事情。