为什么我得到OutOfMemory异常?

时间:2012-01-19 13:25:49

标签: java android

我得到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);
            }

4 个答案:

答案 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。

您是否尝试过opening it with append option

答案 3 :(得分:0)

你得到OOME是因为你加载了整个文件,然后得到了一些字符串。相反,请跳过输入流并阅读。