1

我必须阅读一个大文本文件(大约 5 兆字节)。

为了阅读我使用的这个文件,BufferedReader()但它会导致内存泄漏和堆增长,是否有任何替代选项来优化我的代码?

            StringBuffer sb = new StringBuffer();
            BufferedReader reader = new BufferedReader(new FileReader(vCache));
            String line = null;

            while ((line = reader.readLine()) != null) 
            {
                sb.append(line);
            }
4

6 回答 6

1

尝试使用InputStream而不是BufferedReader

try {
    InputStream is = new FileInputStream(vCache);
    byte[] b = new byte[is.available()];
    is.read(b);
    String text = new String(b);
}
于 2013-06-03T11:13:07.797 回答
1

我猜你正在阅读本地文件。在这种情况下,您最好将整个文件读入字节数组,然后转换为字符串:

InputStream is = new FileInputStream(vCache);
byte[] buffer = new byte[is.available()];
is.read(buffer);
is.close();
jsonContent = new String(buffer, "UTF-8");

但是,通过将 Android 中如此大的文件读入内存,您可能仍然会遇到问题。我想说,如果您需要读取一个 5 MB 的 json 文件,那么您可能没有正确构建您的应用程序。

于 2013-06-03T11:13:10.403 回答
1

默认bufferSize使用的BufferedRedaer是 is 8KB,但是由于您是逐行阅读,因此累积会更多。为了改善这一点,您可以使用:

BufferedReader(Reader in, int sz)<-- 使用sz较小的值说4KB

read(char[] cbuf)<-- 约束cbuf大小作为阅读器大小

close()<-- 现在可以对读取器实例持有的任何内存进行 GC

现在,您的代码StringBuffer sb将所有行都保存在完整的文件内容中,即使在进行上述更改后,如果所需的内存 (~fileSize) 不可用于JVM,您将再次陷入 OOM 问题。我不确定你是否是这种情况,否则上面应该会稍微改善本地内存峰值。

于 2013-06-03T11:27:12.797 回答
0

您正在解析 JSON。

如果输入文件存在,您可以通过删除美化(例如缩进、换行符等)来缩小输入文件。

您还可以尝试直接从流中读取的解析器,希望它不需要一次缓冲所有内容。例如,Android 提供了JsonReader,它允许您自己解析流并控制数据结构,这意味着您可以使用内存效率更高的结构,并且它也不会缓冲整个流。不幸的是,它是在 API 级别 11 中添加的,因此向后兼容性可能是一个问题。

一种替代方法是,如果顶级对象是一个数组,则将其拆分为几个较小的数组,可能在不同的文件中,分别解析它们并合并子数组。如果基础对象具有相似的结构,您可以在合并之前将它们转换为 Java 对象,这将具有更紧凑的内存结构。

于 2013-06-03T11:13:00.030 回答
0

发送 JSON,使每一行对应一个完整的 db 行和格式良好的 json。这样您就不必一起处理整个文件。

//StringBuffer sb = new StringBuffer();
BufferedReader reader = new BufferedReader(new FileReader(vCache));
String line = null;

while ((line = reader.readLine()) != null)  {
  //Parse JSON
  //Insert into local SQLite DB.
}
于 2013-06-03T11:58:27.357 回答
0

您的代码...所写...读取行并将它们累积在StringBuilder. 您正在累积行的事实是内存泄漏的一种形式。

防止泄漏的最佳方法是将您的应用程序更改为像这样工作:

    BufferedReader reader = new BufferedReader(new FileReader(vCache));
    String line = null;
    while ((line = reader.readLine()) != null) {
        process(line);
    }

换句话说,不要在内存中累积行数。在阅读它们时处理它们,然后丢弃它们。


如果您的处理是这样的,您必须在内存中累积行,那么如果您StringBuilder这样分配,您将获得更好的内存使用:

    StringBuilder sb = new StringBuilder(fileSizeInCharacters);

这将避免重新分配的需要,这可能(在最坏的情况下)需要的字符数是文件大小(以字符为单位)的 3 倍。

但是,您迟早会遇到同样的问题。在内存中累积文件内容不会扩展。


您的评论表明这确实是一个 JSON 处理问题。这是关于流式 JSON 处理主题的问答:

流式 API 的想法是您不需要将 JSON“对象”转换为代表整个事物的内存树结构。

于 2013-06-03T11:51:27.300 回答