将文本文件加载到List时出现内存不足错误,即使我已指定足够大的xmx

时间:2012-02-07 17:13:12

标签: java list collections out-of-memory heap-memory

当我尝试使用-Xms32m -Xmx128m在List中加载39MB文本时出现内存不足错误。所以我开始一点一点地增加Xmx直到它成功加载并发现我需要至少Xmx170m来加载内存中的39MB文件

我想知道,为什么我需要如此大量的内存?我尝试使用UTF-8,UTF-16和UTF-32计算列表中分配的内存量,但是在获得内存不足异常时,它们似乎都没有匹配Xmx。那么计算分配内存的正确方法是什么?

有人可以解释一下我在这里缺少什么吗?

以下是-Xms32m -Xmx128m

的输出和代码示例
Max memory 129 MB.
Total memory 32 MB.
Free memory 32 MB.
Input file size 39 MB.
Out Of Memory Error
List size in UFT-8 29 MB.
List size in UFT-16 58 MB.
List size in UFT-32 116 MB.
Free memory 4 MB.
End 

java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.ArrayList.ensureCapacity(Unknown Source)
    at java.util.ArrayList.add(Unknown Source)
    at com.nrx.util.SortUtil.main(SortUtil.java:288)



public static void main(String[] args)
{
    System.out.println("Max memory "+Runtime.getRuntime().maxMemory()/1000 /1000+" MB.");
    System.out.println("Total memory "+Runtime.getRuntime().totalMemory()/1000 /1000+" MB.");
    System.out.println("Free memory "+Runtime.getRuntime().freeMemory()/1000 /1000+" MB.");

    long utf8 = 0;
    long utf16 = 0;
    long utf32 = 0;
    List<String> strList = new ArrayList<String>();
    try 
    {
        File inFile = new File("data/input38.log");
        System.out.println("Input file size "+inFile.length()/1000 /1000+" MB.");
        BufferedReader fileReader = new BufferedReader(new FileReader(inFile));
        String line = fileReader.readLine();
        while (line != null)
        {
            utf8 = utf8 + line.getBytes("UTF-8").length;
            utf16 = utf16 + line.getBytes("UTF-16").length;
            utf32 = utf32 + line.getBytes("UTF-32").length;

            StringTokenizer st = new StringTokenizer(line, " ");
            while(st.hasMoreTokens())
                strList.add(st.nextToken().trim());
            line = fileReader.readLine();
        }

    } 
    catch (OutOfMemoryError e) 
    {
        System.out.println("Out Of Memory Error ");
        System.out.println("List size in UFT-8 "+utf8/1000 /1000+" MB.");
        System.out.println("List size in UFT-16 "+utf16/1000 /1000+" MB.");
        System.out.println("List size in UFT-32 "+utf32/1000 /1000+" MB.");
        System.out.println("Free memory "+Runtime.getRuntime().freeMemory()/1000 /1000+" MB.");
        e.printStackTrace();
    }
    catch (FileNotFoundException e) 
    {
        e.printStackTrace();
    } 
    catch (IOException e) 
    {
        e.printStackTrace();
    }
    System.out.println("End ");
}

4 个答案:

答案 0 :(得分:2)

我认为这是因为您使用的是ArrayList。 ArrayList是简单数组的智能包装器。当列表成长时,ArrayList会创建新数组并将旧内容复制到新内容。首先,它非常没有效率。其次,每次都要求列表三倍大小:旧数组中的n个元素和新数组中的n * 2个元素。

因此,请尝试使用LinkedList。我希望它对你有用。

答案 1 :(得分:1)

您正在使用ArrayList。所以这是一个基于数组的列表。没有创建新的,更大的数组,就无法改变数组的大小。需要分配新数组,并且需要将所有元素复制到较大的数组(使用一些空白空间来添加一些不太重的元素)。尝试使用具有指定数量元素的String []表来最小化它在内存中的大小并避免数组复制。

我不确定,但我认为在Java中,字符串中的字符总是16位?

Java中的字符串是共享和优化的,因此计算字符串的大小并非易事。

编辑: 我看到有人提到了LinkedList,请注意,在该列表中总有其他指针变量也需要存储在内存中。

答案 2 :(得分:0)

正如其他人所指出的那样,即使文件大小很小,你也可能需要额外的内存来复制数组列表。

为了获得更好的图片,您可以尝试查找每增加10 MB文件大小所需的额外内存量。无论文件大小如何,都需要一定数量的内存。

其次,您还应该在完成GC后测量内存。你可以看到使用JVisualVm占用内存的地方。

答案 3 :(得分:0)

ArrayList的默认容量是10,之后它的容量加倍,所以如果你有100 000个元素,实际分配的容量可以是200 000,这可能是造成这种异常的原因