在Java中使用BufferedReader读取大文件

3
我了解在Java中读取大型文本文件有两种方法。一种是使用Scanner,另一种是使用BufferedReader。
Scanner reader = new Scanner(new FileInputStream(path));
while (reader.hasNextLine()){
    String tempString = reader.nextLine();
    System.out.println(java.lang.Runtime.getRuntime().totalMemory()/(1024*1024.0));
}

需要打印的数字始终在某个值左右波动。

然而,当我按照下面的修改使用bufferedReader时,数字不稳定,可能在一行中突然增加(约20MB),然后在接下来的许多行中保持相同(如8000行)。这个过程会重复。

更新 我在这里输入了使用BufferedReader的第二种方法,以下是正确的写法:

BufferedReader reader = new BufferedReader
    (new InputStreamReader(new FileInputStream(path)),5*1024*1024);
for(String s = null;(s=reader.readLine())!=null; ){
    System.out.println(java.lang.Runtime.getRuntime().totalMemory()/(1024*1024.0));
}

或者使用 while 循环
String s;
while ((s=reader.readLine())!=null ){
    System.out.println(java.lang.Runtime.getRuntime().totalMemory()/(1024*1024.0));
}

更具体地说,这里是读取250M文件的测试用例结果。
扫描器案例: 行号-总内存 5000-117.0 10000-112.5 15000-109.5 20000-109.5 25000-109.5 30000-109.5 35000-109.5 40000-109.5 45000-109.5 50000-109.5
缓冲读取器案例: 行号-总内存 5000-123.0 10000-155.5 15000-155.5 20000-220.5 25000-220.5 30000-220.5 35000-220.5 40000-220.5 45000-220.5 50000-211.0
然而,扫描器速度较慢,这就是我试图避免使用它的原因。
另外,我检查了缓冲读取器的情况,发现内存总量突然在一个随机行中增加。

可能是由于BufferedReader后面的缓冲区管理问题... - Jean-Baptiste Yunès
3
你的第二个循环是一个繁忙的循环,不断获取和打印总内存。它读取一行文本,然后不停地循环。 - JB Nizet
当你让BufferedReader使用5 MB缓冲区时,你并没有在真正比较苹果与苹果。此外,你不会看到大缓冲区的性能提升。你可以将缓冲区大小降低到4 KB或者16 KB,而不会有性能损失。你会发现,即使你只使用默认缓冲区大小,BufferedReader也比Scanner要快得多。 - Andreas
1
totalMemory对于你(可能)想要学习的内容完全无关紧要。请尝试使用freeMemory。 - laune
谢谢大家,但我仍然感到困惑,我并不是要比较他们使用的内存,我只是想知道为什么使用bufferedReader的内存会增加,并在单个随机行中突然增加。 - Zheyu Ji
1个回答

3
仅仅使用Scanner不能很好地处理大型文本文件。Scanner和BufferedReader不能相提并论。你可以在Scanner中使用BufferedInputStream,这样就有了同样的功能,而Scanner添加了许多“流”读取功能,而不仅仅是行。查看totalMemory并不特别有用。引用Javadoc:返回Java虚拟机中的总内存量。该方法返回的值可能会随时间而变化,具体取决于主机环境。尝试使用freeMemory,这更有趣,反映每隔一段时间发生的GC阶段。稍后评论Scanner的速度慢:读取一行只需要扫描行分隔符的字节,这就是BufferedReader所做的。然而,Scanner为此任务启动了java.util.regex.Matcher(因为它更适合其整体设计)。仅使用Scanner读取行是不必要的。

是的,我知道Scanner和BufferedReader不可比较。但我只想知道为什么使用Scanner可以保持总内存稳定,而使用BufferedReader会导致总内存增加。如果文本文件超过100GB,我仍然可以使用Scanner,但使用BufferedReader则会超出内存限制。 - Zheyu Ji
@ZheyuJi 不会的。根据您发布的代码,每行都将被垃圾回收。在尝试解决问题之前,您应该确认您实际上是否有担忧的问题。很可能Scanner在内部使用了BufferedReader - user207421
@EJP,谢谢,但趋势表明如果您可以查看我更新的问题版本,则总内存确实会增加。实际上,我在云计算中遇到了这个问题,当我使用扫描器时,它完全正常但速度较慢,当我使用缓冲读取器时,它将超出内存。我的代码中的其他逻辑相同。 - Zheyu Ji
这段数据没有表现出任何趋势。没有内存不足的情况,而且在20000行后也没有增加:相反,它非常稳定。使用BufferedReader每秒可以读取数百万行数据,我已经多次这样做了。 - user207421
@EJP 感谢您,您说得非常正确。在我的本地机器上,使用更大的文件是稳定的。但是在云端测试中仍然显示超出内存限制,我不得不每50000行添加System.gc()来解决这个问题。无论如何,我的主要问题已经解决,我还需要一些时间来弄清楚为什么它在云端失败而没有gc。 - Zheyu Ji
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - laune

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