Java堆空间:Hashmap,ArrayList

时间:2012-03-08 14:20:50

标签: java arraylist hashmap heap

我想处理一个文本文件(大约400 MB),以便从每行中给出的数据创建一个递归的父子结构。必须为自上而下的导航准备数据(输入:父级,输出:所有子级和子级子级)。例如。要阅读的行: (儿童下,ID1,ID2,的下,ID3)

132142086 ; 1; 2;的 132528589 ; 132528599
132142087 ; 1; 3;的 132528589 ; 132528599
132142088 ; 1; 0;的 132528589 ; 132528599
323442444 ; 1; 0;的 132142088 ; 132528599
454345434 ; 1; 0;的 323442444 ; 132528599个

132528589:是132142086,132142087,132142088的父母 132142088:是323442444的父母 323442444:是454345434的父母

鉴于:OS windows xp,32bit,2GB可用内存和-Xmx1024m 这是我准备数据的方式:

HashMap<String,ArrayList<String>> hMap=new HashMap<String,ArrayList<String>>();
  while ((myReader = bReader.readLine()) != null) 
          {
             String [] tmpObj=myReader.split(delimiter);
                   String valuesArrayS=tmpObj[0]+";"+tmpObj[1]+";"+tmpObj[2]+";"+tmpObj[3]+";"+tmpObj[4];
                        ArrayList<String> valuesArray=new ArrayList<String>();
                        //case of same key
                        if(hMap.containsKey(tmpObj[3]))
                            {
                            valuesArray=(ArrayList<String>)(hMap.get(tmpObj[3])).clone();
                            }

                        valuesArray.add(valuesArrayS);
                        hMap.put(tmpObj[3],valuesArray);
                        tmpObj=null;
                        valuesArray=null;
                        }

return hMap;

之后我使用递归函数:

HashMap<String,ArrayList<String>> getChildren(input parent)

用于创建所需的数据结构。计划是使用函数getChildren让hMap可用于多个线程(只读)。 我用90 MB的输入文件测试了这个程序,它似乎工作正常。但是,使用超过380 MB的真实文件运行它会导致:
线程“main”中的异常java.lang.OutOfMemoryError:Java堆空间
我需要一些内存资源管理方面的帮助

4 个答案:

答案 0 :(得分:2)

从“简单的方法”方面来看:基于你的问题陈述,你不需要保持id1,id2或id3。假设是这种情况,如何用HashMap<String, ArrayList<String>>替换HashMap<Integer, ArrayList<Integer>>?您可以使用Integer.parseInt()进行字符串到int的转换,并且Integer应始终小于相应的String。

其他建议:如果您不关心重复项,请将ArrayList替换为HashSet

根据outofBounds的回答,每次要向其添加项目时都不需要克隆ArrayList

答案 1 :(得分:2)

根据他人的建议,检查增加记忆力。此外,您可以更好地按照Sbodd和其他人的建议将数据存储在表格中。

但是,您可能会遇到内存碎片问题。散列图使用数组。大哈希映射使用大数组。您没有指定hashmap的大小,所以每次它确定它需要更大时,它会丢弃它的旧数组并分配一个新数组。过了一会儿,你的内存将填满丢弃的哈希表数组,即使你在技术上有足够的可用内存,你也会得到一个OutOfMemoryException。 (90%的记忆可以使用,但是太小而无法使用。)

垃圾收集器(GC)将持续工作,将所有这些空闲位组合成足够大的块来使用。如果你的程序运行得足够慢,那么你就不会有问题,但你的程序正在全速运行,而GC将会落后。如果GC无法足够快地组装一个空闲块,GC将抛出异常;记忆存在的事实并不能阻止它。 (这意味着可以运行的程序不会,但它会使JVM无法正常运行,并且对用户来说看起来很糟糕。)

鉴于您知道哈希映射的大小,我会预先设置大小。即使大小不正确,它也可以在不增加堆大小的情况下解决内存问题,并且肯定会使程序运行得更快(或者与文件读取速度一样快 - 使用 big 文件缓冲区)。

如果你不知道你的桌子有多大,可以使用TreeMap。它有点慢,但没有分配庞大的数组,因此对GC来说更加友善。我发现它们很多更灵活,更实用。您甚至可以查看比TreeMap慢的ConcurrentSkipTreeMap,但允许您同时添加,读取和删除多个线程。

但你最好的选择是:

hMap = new HashMap<String,ArrayList<String>>( 10000000 );

答案 2 :(得分:0)

在你的While循环中,你可以减少像这样的空间

String [] tmpObj=myReader.split(delimiter);
// String = String + String takes more Space than String.format(...)
//String valuesArrayS=tmpObj[0]+";"+tmpObj[1]+";"+tmpObj[2]+";"+tmpObj[3]+";"+tmpObj[4];

// Just Adding if thers is no List for a Key
if(!hMap.containsKey(tmpObj[3]){
    hMap.put(tmpObj[3], new ArrayList<String>());
}
// Gettin the list from the Map and adding the new stuff
List<String> values = hMap.get(tmpObj[3]);
values.add(String.format("%s;%s;%s;%s;%s",tmpObj[0], tmpObj[1], tmpObj[2], tmpObj[3], tmpObj[4]));

无需克隆列表

答案 3 :(得分:0)

您正在测试1GB内存可以做什么的界限。

你可以:

  1. 增加堆空间。 32位窗口会限制你大约1.5GB,但是你仍然有一点摆动空间可能足以让你超越顶部。
  2. 构建某种类型的预处理器实用程序,该实用程序按照您知道的工作大小对文件进行预分区,并逐个对其进行操作, 也许是分层次的。
  3. 尝试重新构建您的程序。它有很多分裂和连接。在java中,字符串是不可变的,当你 拆分字符串并与正在创建的+运算符连接 新的字符串一直在这10个案例中有9个没关系,但是 在您使用非常有限的资源集合的情况下,它可能会产生影响)
  4. 作为一个不太有帮助的一面。这里真正的问题是你没有足够的资源来处理这个任务,而优化只会带你到目前为止。它就像是问如何用花园抹子更好地穿过一座山。真正的答案可能是你不想听到的那个扔掉抹子并投资一些工业设备的那个

    在第二个更有帮助的注意事项(如果你像我一样有趣) - 你可以尝试将jVisualVM挂钩到你的应用程序并试图了解你的堆积进展或使用jhat-XX:+HeapDumpOnOutOfMemoryError jvm标志,用于查看崩溃时堆发生了什么。