Java高效读取大文件

1、概述

本教程将演示如何用Java高效地读取大文件。

2、在内存中读取

读取文件行的标准方式是在内存中读取,Guava 和Apache Commons IO都提供了如下所示快速读取文件行的方法:

Files.readLines(new File(path), Charsets.UTF_8);
 
FileUtils.readLines(new File(path));

这种方法带来的问题是文件的所有行都被存放在内存中,当文件足够大时很快就会导致程序抛出OutOfMemoryError 异常。

例如:读取一个大约1G的文件:

@Testpublic void givenUsingGuava_whenIteratingAFile_thenWorks() throws IOException {    String path = ...    Files.readLines(new File(path), Charsets.UTF_8);}

这种方式开始时只占用很少的内存:(大约消耗了0Mb内存)

[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 128 Mb
[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 116 Mb

然而,当文件全部读到内存中后,我们最后可以看到(大约消耗了2GB内存)

[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 2666 Mb
[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 490 Mb

这意味这一过程大约耗费了2.1GB的内存——原因很简单:现在文件的所有行都被存储在内存中。

把文件所有的内容都放在内存中很快会耗尽可用内存——不论实际可用内存有多大,这点是显而易见的。

此外,我们通常不需要把文件的所有行一次性地放入内存中——相反,我们只需要遍历文件的每一行,然后做相应的处理,处理完之后把它扔掉。所以,这正是我们将要做的——通过行迭代,而不是把所有行都放在内存中。

3、文件流

现在让我们看下这种解决方案——我们将使用java.util.Scanner类扫描文件的内容,一行一行连续地读取:

FileInputStream inputStream = null;Scanner sc = null;try {    inputStream = new FileInputStream(path);    sc = new Scanner(inputStream, "UTF-8");    while (sc.hasNextLine()) {        String line = sc.nextLine();        // System.out.println(line);    }    // note that Scanner suppresses exceptions    if (sc.ioException() != null) {        throw sc.ioException();    }} finally {    if (inputStream != null) {        inputStream.close();    }    if (sc != null) {        sc.close();    }}

这种方案将会遍历文件中的所有行——允许对每一行进行处理,而不保持对它的引用。总之没有把它们存放在内存中(大约消耗了150MB内存)

[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Total Memory: 763 Mb
[main] INFO  org.baeldung.java.CoreJavaIoUnitTest - Free Memory: 605 Mb

4、Apache Commons IO流

同样也可以使用Commons IO库实现,利用该库提供的自定义LineIterator:

LineIterator it = FileUtils.lineIterator(theFile, "UTF-8");try {    while (it.hasNext()) {        String line = it.nextLine();        // do something with line    }} finally {    LineIterator.closeQuietly(it);}

由于整个文件不是全部存放在内存中,这也就导致相当保守的内存消耗:(大约消耗了150MB内存)

[main] INFO  o.b.java.CoreJavaIoIntegrationTest - Total Memory: 752 Mb
[main] INFO  o.b.java.CoreJavaIoIntegrationTest - Free Memory: 564 Mb

5、结论

这篇短文介绍了如何在不重复读取与不耗尽内存的情况下处理大文件——这为大文件的处理提供了一个有用的解决办法。

原文发布于微信公众号 - Java团长(javatuanzhang)

原文发表时间:2017-11-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏猿天地

知识点-Spring Boot 统一异常处理汇总

上面讲的是做页面开发的时候遇到的问题,还有一种情况就是用来开发Rest接口,当错误的时候我们希望返回给用户的是我们接口的标准格式,不是返回一段html代码。

17020
来自专栏xingoo, 一个梦想做发明家的程序员

C/C++ 遇到0xcccccccc访问冲突

最近一直在纠结这个问题. ? 最近写代码,总是遇到这个问题,一旦遇到这个问题,以前好使的代码也就不好使了。很费解,上网搜集了下资料.... 这个0xcccccc...

25370
来自专栏一个会写诗的程序员的博客

8.3 Spring Boot集成Scala混合Java开发参考资料

本章我们使用Spring Boot集成Scala混合Java开发一个Web性能测试平台。

21010
来自专栏邹立巍的专栏

Linux 的进程间通信:信号量

Linux环境下主要实现的信号量有两种。根据标准的不同,它们跟共享内存类似,一套XSI的信号量,一套POSIX的信号量。下面我们分别使用它们实现一套类似文件锁的...

62200
来自专栏代码拾遗

深入理解Spring MVC

使用Spring Boot和web,thymeleaf的starter来设置初始工程。xml配置如下:

12620
来自专栏一个会写诗的程序员的博客

《Kotlin极简教程》第2章 快速开始:HelloWorld

我们在本章将介绍使用Kotlin程序设计语言,分别使用命令行、应用程序、Web RESTFul、Android、Kotlin JavaScript等方式向世界问...

12830
来自专栏C/C++基础

CMake简介及使用实例

CMake是一个跨平台的建构系统的工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的构建文档makefile或者project文件,描...

21020
来自专栏一个会写诗的程序员的博客

《Spring Boot极简教程》第9章 Spring Boot集成Scala混合Java开发参考资料

本章我们使用Spring Boot集成Scala混合Java开发一个Web性能测试平台。

23520
来自专栏java学习

Spring4+Spring MVC+MyBatis整合思路

这个很简单,只需要web容器中注册org.springframework.web.context.ContextLoaderListener,并指定spring...

11630
来自专栏柠檬先生

SpringMVC——笔记

使用 @RequestMapping 映射请求 Spring MVC 使用@RequestMapping 注解为控制器指定可以处理那些URL请求。   在控制器...

24650

扫码关注云+社区

领取腾讯云代金券