我有以下代码,我正在使用stringBuffer对象读取大文件,通过创建临时byte []对象执行某些操作,因此,当文件大小较大时,我会遇到内存不足异常说16 MB。
StringBuffer dataBuffer;
ArrayList<byte[]> sourceFragments;
ArrayList<BitSet> sourceBits = new ArrayList<BitSet>();
dataBuffer = eHelper.readFile(encoder.getFileName());
sourceFragments = eHelper.fragmentFile(dataBuffer.toString());
/*
* converting byte[] to BitSet
the below loop is run 128 times
*/
Iterator<byte[]> iter = sourceFragments.iterator();
while (iter.hasNext()) {
byte[] temp = iter.next();
// temp.length will return 128 KB
sourceBits.add(eHelper.byteArrayToBitSet(temp));
}
如果我有办法防止这种情况发生,那么我很想知道这种情况。我没有考虑增加堆空间的选项,我在32位机器上使用默认堆空间。有什么办法可以减少正在创建的临时对象的数量,这样我就可以避免outOfMemory Exception
EDIT1:
我对代码进行了以下更改,我将npt作为String加载整个文件到内存中,我不创建byte []数组,而是直接从文件中读取并直接将其转换为arrayList比特集。这有助于我能够处理20 MB的文件,我想知道是否可以进一步推动更多的工作与最多30 MB的文件?
edit2:
我已经修改了如下的源代码,我删除了我创建的所有冗余数据类型 public ArrayList fragmentSourceData(File filename){ RandomAccessFile r; ArrayList sourceBits = new ArrayList();
try {
r= new RandomAccessFile(filename, "r");
System.out.println(r.length());
encoder.setSourceFileLength((int)r.length());
int fragmentSize = encoder.calculateFragmentSize();
System.out.println(fragmentSize);
encoder.setFragmentSize(fragmentSize);
encoder.setParameters();
byte[] b = new byte[fragmentSize] ;
long new_pos=0;
int i=0;
while(new_pos<=encoder.getSourceFileLength()){
i++;
r.read(b ,0, fragmentSize );
new_pos=fragmentSize*i;
r.seek(new_pos);
sourceBits.add(BitSet.valueOf(b));
}
r.close();
b=null;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return sourceBits;
}
}
答案 0 :(得分:1)
fragmentFile
的作用也不明显。它如何将字符串转换为byte[]
片段?
答案 1 :(得分:1)
答案 2 :(得分:0)
代码中有一些改进的地方。让我们关注循环。
Iterator<byte[]> iter = sourceFragments.iterator();
while (iter.hasNext()) {
byte[] temp = iter.next();
// temp.length will return 128 KB
sourceBits.add(eHelper.byteArrayToBitSet(temp));
}
无需获取ArrayList sourceFragments的迭代器。您可以将while循环转换为for循环,并简单地循环ArrayList中的每条记录。这些变化如下所示。
for(byte[] val : sourceFragments){
sourceBits.add(eHelper.byteArrayToBitSet(val));
}
答案 3 :(得分:0)
嗯,默认内存限制可能是64M(取决于你的JVM),所以如果你正在将16M文件读入内存,那么将其转换为List<byte[]>
也需要16M然后转换它变成List<BitSet>
也需要16M,然后你肯定会推动极限,因为可能还有其他事情需要一些记忆。
将dataBuffer
的内容转换为List<byte[]>
后,您可以将dataBuffer
显式设置为null。然后,在循环中,而不是使用迭代器,您可以以老式的方式循环List,允许您在将每个元素转换为BitSet后将其显式设置为null。内存压力应触发GC循环,以清理这些未使用的数据结构。
答案 4 :(得分:0)
将整个文件读入stringbuffer似乎是浪费内存,除非你以后需要它。
由于您需要完整的16MB(内存中更多)来存储文件的位,因此您必须在生成阶段节省一些内存。
我不确定eHelper是什么类型的对象。 如果sourcebits必须碎片化,你可以尝试类似:
BITSET_MAX_SIZE = ...;
File file = new File("somefile");
int total = file.length();
InputStream in = new BufferedInputStream(new FileInputStream(file));
for (int bytesRead = 0; bytesRead < total;) {
int currBitsetSize = Math.min(BITSET_MAX_SIZE, (total - read) * 8); // Can this be variable or should it be padded?
BitSet bitset = new Bitset(currBitsetSize);
for (int bitsetIndex = 0; bitsetIndex < currBitsetSize; bitsetIndex += 8) {
int currByte = in.read();
bytesRead++;
for (int bitPos = 0; bitPos < 8; bitPos++) {
if ((currByte & (1 << i)) > 0) {
bitset.set(bitsetIndex + i); // Set the position to 1
}
}
}
sourceBits.add(bitset);
}
in.close();
我没有尝试过这个我自己,但这样的事情可能有用。对不起,如果它不是最漂亮的例子。
也许您不能直接使用新的FileInputStream,具体取决于源,但您应该获得一个输入流并逐字节地读取。
这段代码肯定可以改进,因为它不是最有效的。您可能希望使用in.read(byte [] buffer,int byteOffset,int byteCount)读取。