ero*_*ppa 11 java memory performance file-io file
我有以下代码读取跟随文件,将\ r \n附加到每行的末尾并将结果放入字符串缓冲区:
public InputStream getInputStream() throws Exception {
StringBuffer holder = new StringBuffer();
try{
FileInputStream reader = new FileInputStream(inputPath);
BufferedReader br = new BufferedReader(new InputStreamReader(reader));
String strLine;
//Read File Line By Line
boolean start = true;
while ((strLine = br.readLine()) != null) {
if( !start )
holder.append("\r\n");
holder.append(strLine);
start = false;
}
//Close the input stream
reader.close();
}catch (Throwable e){//this is where the heap error is caught up to 2Gb
System.err.println("Error: " + e.getMessage());
}
return new StringBufferInputStream(holder.toString());
}
Run Code Online (Sandbox Code Playgroud)
我尝试读取400Mb文件,并将最大堆空间更改为2Gb,但它仍然给出了内存堆异常.有任何想法吗?
Ada*_*ski 21
它可能与StringBuffer
达到容量时调整大小的方式有关 - 这涉及创建一个char[]
与前一个大小相当的新双倍,然后将内容复制到新数组中.与已经将Java中的字符存储为2个字节的点一起,这肯定会增加您的内存使用量.
要解决此问题,您可以创建一个StringBuffer
具有足够容量的开头,因为您知道文件大小(因此可以读取大致的字符数).但是,请注意,如果您尝试将此大型StringBuffer
转换为a,则也会发生阵列分配String
.
另一点:你通常应该赞成StringBuilder
,StringBuffer
因为它上面的操作更快.
您可以考虑实现自己的"CharBuffer",例如使用LinkedList
char []来避免昂贵的数组分配/复制操作.您可以创建此类实现CharSequence
,也许可以避免String
完全转换为.更简洁表示的另一个建议:如果您正在阅读包含大量重复单词的英文文本,您可以阅读并存储每个单词,使用该String.intern()
功能可以显着减少存储空间.
Dav*_*veR 13
首先,Java字符串是UTF-16(即每个字符2个字节),因此假设您的输入文件是ASCII或类似的每字符一个字节格式,那么holder
输入数据的大小将是输入数据的2 倍,加上额外的\r\n
每行和任何额外的开销.假设StringBuffer中的存储开销非常低,那么直接有大约800MB.
我还可以相信文件的内容被缓冲两次 - 一次在I/O级别,一次在BufferedReader中.
但是,要确切地知道,最好查看堆上的实际内容 - 使用像HPROF这样的工具来查看内存的确切位置.
我解决这个问题的条件,我建议你一次处理一行,在你添加行终止后写出每一行.这样你的内存使用量应该与行的长度成比例,而不是整个文件.
Chr*_*Rea 12
这是一个有趣的问题,但是为什么不尝试一种不需要程序将整个文件加载到内存中的设计,而不是强调Java为什么要使用这么多内存?
Mic*_*rdt 11
你有很多问题:
StringBuffer.toString()
暂时将占用的内存加倍,因为它会复制所有这些组合意味着您可以在RAM中暂时需要最多8倍的文件大小,即400M文件的3.2G.即使您的计算机在物理上拥有那么多RAM,它也必须运行64位操作系统和JVM来实际为JVM获取那么多堆.
总而言之,在内存中保留如此庞大的字符串只是一个可怕的想法 - 而且它完全不必要 - 因为你的方法返回一个InputStream
,你真正需要的只是一个FilterInputStream,它可以动态添加换行符.