首先,我会尝试解释我需要做什么。 我需要读取一个文件(其大小可以从1字节到2 GB),最大2 GB,因为我尝试使用MappedByteBuffer进行快速读取。也许以后我会尝试以块的形式读取文件,以便读取任意大小的文件。
当我读取文件时,我转换其字节并将它们(使用 ASCII 编码)转换为字符,稍后我将其放入 StringBuilder 然后我将其设置为 String Builder 到 ArrayList
但是我还需要做以下事情:
用户可以输入blockSize
这是我必须读入StringBuilder的字符数(基本上是转换为字符的文件字节数)
收集用户定义的字符数后,我创建了字符串构建器的副本并将其放入数组列表
为每个char读取执行所有步骤。问题出在String Builder上,因为如果文件很大(<500 MB),我会得到异常 OutOfMemoryError 。
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:45)
at java.lang.StringBuilder.<init>(StringBuilder.java:80)
at java.lang.StringBuilder.<init>(StringBuilder.java:106)
at borrows.wheeler.ReadFile.readFile(ReadFile.java:43)
Java Result: 1
我发布了我的代码,也许有人可以建议对此代码进行改进或提出一些替代方法。
public class ReadFile {
//matrix block size
public int blockSize = 100;
public int charCounter = 0;
public ArrayList readFile(File file) throws FileNotFoundException, IOException {
FileChannel fc = new FileInputStream(file).getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size());
ArrayList characters = new ArrayList();
int counter = 0;
StringBuilder sb = new StringBuilder();//blockSize-1
while (mbb.hasRemaining()) {
char charAscii = (char)mbb.get();
counter++;
charCounter++;
if (counter == blockSize){
sb.append(charAscii);
characters.add(new StringBuilder(sb));//new StringBuilder(sb)
sb.delete(0, sb.length());
counter = 0;
}else{
sb.append(charAscii);
}
if(!mbb.hasRemaining()){
characters.add(sb);
}
}
fc.close();
return characters;
}
}
修改: 我在做Burrows-Wheeler转型。在那里我应该读取每个文件然后通过块大小创建尽可能多的所需矩阵。我相信维基会比我更好地解释:
http://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform
答案 0 :(得分:1)
如果你加载大文件,你的内存不足就不足为奇了。
你有多少记忆?您是否使用64位Java 64位系统?你分配了多少堆内存(例如使用-Xmx
设置)?
请记住,您需要至少两倍于文件大小的内存,因为Java使用Unicode UTF-16,每个字符使用至少2个字节,但您的输入是每个字符一个字节。因此,要加载2GB文件,您需要至少 4GB分配给堆,仅用于存储此文本数据。
此外,您需要整理代码中的逻辑 - 您在sb.append(charAscii)
和if
中执行相同的else
,并在每个!mbb.hasRemaining()
中测试while((mbb.hasRemaining())
迭代blockSize
循环。
正如我在上一个问题中提到的那样,您是否需要存储StringBuilders,或者生成的字符串是否正常?存储字符串可以节省空间,因为StringBuilder以大块的形式分配内存(我认为每次空间用完时它的大小都会增加一倍!)因此可能会浪费很多。
如果必须使用StringBuilders,那么将它们预先调整为{{1}}的值将使代码更具内存效率(并且更快)。
答案 1 :(得分:1)
我尝试使用MappedByteBuffer进行快速阅读。也许以后我会试试 以块的形式读取文件以读取任意大小的文件。
当我读取文件时,我转换其字节并转换它们(使用ASCII 编码)到后来我放入StringBuilder然后我的字符 将此String Builder放入ArrayList
这听起来更像是一个问题,而不是解决方案。我建议您该文件已经是ASCII或字符数据;可以使用BufferedReader非常有效地读取它;并且它可以一次处理一行。
那样做。通过使用MappedByteBuffer,您甚至无法获得双倍的速度,而您正在做的所有事情(包括MappedByteBuffer)都在以真正的英雄规模消耗内存。
如果文件不能逐行处理或按记录记录,则上游存在严重错误。