我有以下代码,它读取以下文件,在每一行的末尾附加一个\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());
}我尝试读入一个400Mb的文件,并将最大堆空间更改为2 2Gb,但仍然出现内存不足的堆异常。有什么想法吗?
发布于 2009-07-06 21:55:15
这是一个有趣的问题,但与其强调为什么Java使用这么多内存,为什么不尝试一种不需要程序将整个文件加载到内存中的设计呢?
发布于 2009-07-06 21:58:51
这可能与StringBuffer在达到容量时如何调整大小有关-这涉及到创建一个新的char[],它的大小是前一个的两倍,然后将内容复制到新的数组中。再加上关于Java中的字符被存储为2个字节的观点,这肯定会增加内存使用量。
要解决这个问题,您可以创建一个具有足够容量的StringBuffer,前提是您知道文件大小(以及大约要读取的字符数)。但是,需要注意的是,如果您随后尝试将此大型StringBuffer转换为String,也会发生数组分配。
另一点:您通常应该优先使用StringBuilder而不是StringBuffer,因为它的操作速度更快。
您可以考虑实现自己的"CharBuffer",例如使用char[]的LinkedList来避免昂贵的数组分配/复制操作。您可以让这个类实现CharSequence,也许可以完全避免转换为String。关于更紧凑表示的另一个建议是:如果您正在阅读包含大量重复单词的英文文本,则可以读取并存储每个单词,使用String.intern()函数可以显著减少存储空间。
发布于 2009-07-06 21:53:20
首先,Java string是UTF-16 (即每个字符2个字节),因此假设您的输入文件是ASCII或类似的每个字符一个字节的格式,那么holder将是输入数据大小的大约2倍,外加每行额外的\r\n和任何额外的开销。假设StringBuffer中的存储开销非常低,那么马上就会有大约800MB。
我还可以相信您的文件的内容被缓冲了两次-一次在I/O级别,一次在BufferedReader中。
然而,为了确定,最好是查看堆上的实际内容-使用HPROF之类的工具来查看内存到底去了哪里。
为了解决这个问题,我建议你一次处理一行,在添加了行终止符之后写出每一行。这样,内存使用量应该与一行的长度成比例,而不是与整个文件的长度成比例。
https://stackoverflow.com/questions/1089364
复制相似问题