我发现我的程序正在增加的内存是因为下面的代码,目前我正在读一个大约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
}
}
答案 0 :(得分:7)
你还没有真正告诉我们你在做什么,但是:
BufferedReader
行(或从大字符串中获取子字符串),则每个行都可能有一个大的后备缓冲区 - 您可能希望使用maTable.add(new String(postId))
来避免这种情况简而言之,你很可能没有做错任何事情,但是内存增加因素的组合对你不利。其中大部分是不可避免的,但第三个可能是相关的。
答案 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上,单字符串的内存使用情况为:
总计:10个字--40个字节 - 如果您的输入是8位字符集,则可容纳一个char
个数据...或一个byte
个数据。
(这不足以解释你的问题,但无论如何你应该知道它。)
答案 2 :(得分:0)
无法将某些读入内存的数据(来自7G文件)以某种方式释放?艾克乔恩提出的东西......即。因为字符串是不可变的,所以每次读取字符串都需要创建一个新的String对象,如果GC不够快,可能会导致内存不足......
如果是上述情况,则可能会在代码/迭代中插入一些“断点”,即。在某些已定义的点上,发出gc并等到它终止。
答案 3 :(得分:0)
使用 -XX:+ HeapDumpOnOutOfMemoryError 运行程序。然后,您就可以使用像MAT这样的内存分析器来查看正在耗尽所有内存的内容 - 这可能是完全出乎意料的事情。