安卓 - 读取文本文件时出现内存不足错误

4
我正在制作一款Android词典应用程序。在启动时,该应用程序将加载一个.index文件的内容(约2MB,100,000+行)。
然而,当我使用BufferedReader.readLine()并对返回的字符串进行操作时,该应用程序会导致OutOfMemory错误。
// Read file snippet
Set<String> indexes = new HashSet<String)();

FileInputStream is = new FileInputStream(indexPath);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));

String readLine;

while ( (readLine = reader.readLine()) != null) {
    indexes.add(extractHeadWord(readLine));
}

// And the extractHeadWord method
private String extractHeadWord(String string) {
    String[] splitted = string.split("\\t");
    return splitted[0];
}

在阅读日志时,我发现执行过程中会显式地多次触发GC清理对象(GC_EXPLICIT freed xxx objects,其中xxx是一个很大的数字,比如15000、20000等)。

我尝试了另一种方法:

final int BUFFER = 50;
char[] readChar = new char[BUFFER];

//.. construct BufferedReader

while (reader.read(readChar) != -1) {
    indexes.add(new String(readChar));
    readChar = new char[BUFFER];
}

它运行得非常快。但这并不是我想要的。

有没有一种解决方案既像第二个代码片段一样运行得快,又像第一个代码片段一样易于使用?

谢谢。


在代码片段中,DataInputStream有什么用处?看起来你只需要提取字符串而不是原始类型?如果你知道Set的大小,预先分配空间可能会很有帮助,例如new HashSet<String>(100000) - Jeff Foster
@Jeff Foster:感谢您关于HashSet大小分配的建议。 - Genzer
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4513622 这篇文章对于所描述的问题是很好的阅读材料。 - Jeff Foster
2个回答

3

extractHeadWord 使用 String.split 方法。该方法不会创建新的字符串,而是依赖于底层字符串(在您的情况下为 line 对象)并使用索引来指出“新”字符串。

由于您不关心字符串的其余部分,因此需要丢弃它以进行垃圾回收,否则整个字符串将在内存中(但您仅使用其中一部分)。

调用构造函数 String(String)(“复制构造函数”)会丢弃字符串的其余部分:

private String extractHeadWord(String string) {
    String[] splitted = string.split("\\t");
    return new String(splitted[0]);
}

有没有任何文件说明这个?我同意你的观点,但我找不到任何文件记录它。 - Jeff Foster
@Jeff Foster:我刚刚查看了用于String.splitPattern.split源代码,它使用了在String中实现为对substring的调用的Charsequence.subSequence... - dacwe

3
如果您的extractHeadWord函数返回return new String(splitted[0]);,它将不会减少临时对象,但可能会减少应用程序的占用空间。我不知道split是否与substring做了相同的操作,但我猜想是这样的。substring会在原始数据上创建一个新的视图,这意味着完整的字符数组将会保留在内存中。显式调用new String(string)将截断数据。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接