我得到OutOfMemory Exception。为什么?我正在使用此代码进行日志记录。这种方法是否正确? 流的异常和关闭在父方法中处理。
private static void writeToFile(File file, FileWriter out, String message) throws IOException {
if (file.exists() && file.isFile()) {
if ((file.length() + message.getBytes().length) <= FILE_MAX_SIZE_B) {
out.write(message);
} else {
int cutLenght = (int) (file.length() + message.getBytes().length - FILE_MAX_SIZE_B);
FileInputStream fileInputStream = new FileInputStream(file);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
char[] buf = new char[1024];
int numRead = 0;
StringBuffer text = new StringBuffer(1000);
while ((numRead=bufferedReader.read(buf)) != -1) {
text.append(buf,0,numRead);
}
String result = new String(text).substring(cutLenght);
result += message;
FileWriter fileWriter = new FileWriter(file, appendToFile);
writeToFile(file, fileWriter, result);
bufferedReader.close();
}
}
}
编辑:
我正在使用此方法在文件中写入日志。因此,例如在一秒钟内我可以调用10个日志。我在线上得到错误:
while ((numRead=bufferedReader.read(buf)) != -1) {
text.append(buf,0,numRead);
}
答案 0 :(得分:1)
我的猜测是你得到的是OutOfMemoryError,因为一旦它越来越接近最大大小,你正在将日志文件的全部内容读回内存。
你可以用较小的块来读取和写入它,但这可能很棘手,因为你必须避免覆盖你还没有读过的东西。
总的来说,这种技术似乎是维护日志数据的一种非常低效的方法。一些替代方法脱颖而出:
(1)维护一组n个日志文件,每个文件的最大大小为FILE_MAX_SIZE_B / n。当第一个日志填满时,打开下一个日志进行写入,依此类推;当最后一个填满时,回到第一个。这样,每次切换文件时都会丢弃一些最旧的日志数据,但不是全部,而且仍然保持整体大小限制。
(2)在单个文件中旋转数据。每次写入后,添加一个标记,指示这是日志流的结束。当文件达到其最大大小时,只需在开头重新开始,覆盖那里的数据。标记将告诉您最新消息的位置。
答案 1 :(得分:1)
尝试这样的事情:
void appendToFile(File f, CharSequence message, Charset cs, long maximumSize) throws IOException {
long available = maximumSize - f.length();
if (available > 0) {
FileOutputStream fos = new FileOutputStream(f, true);
try {
CharBuffer chars = CharBuffer.wrap(message);
ByteBuffer bytes = ByteBuffer.allocate(8 * 1024); // Re-used when encoding the string
CharsetEncoder enc = cs.newEncoder();
CoderResult res;
do {
res = enc.encode(chars, bytes, true);
bytes.flip();
long len = Math.min(available, bytes.remaining());
available -= len;
fos.write(bytes.array(), bytes.position(), (int) len);
bytes.clear();
} while (res == CoderResult.OVERFLOW && available > 0);
} finally {
fos.close();
}
}
}
可测试:
File f = new File(getCacheDir(), "tmp.txt");
f.delete();
// Or whatever charset you want.
Charset cs = Charset.forName("UTF-8");
int maxlen = 2 * 1024; // For this test, 2kb
try {
for (int i = 0; i < maxlen / 20; i++) {
// Write 30 characters for maxlen/20 times == guaranteed overflow
appendToFile(f, "123456789012345678901234567890", cs, maxlen);
System.out.println("Length=" + f.length());
}
} catch (Throwable t) {
t.printStackTrace();
}
f.delete();
答案 2 :(得分:0)
嗯,你正在尝试将OOM加载到内存中,因为你正在尝试加载OOM。
答案 3 :(得分:0)
你得到OOME是因为你加载了整个文件,然后得到了一些字符串。相反,请跳过输入流并阅读。