当我尝试使用-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 ");
}
答案 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,这可能是造成这种异常的原因