Files.lines()方法使用相关问题

Files.lines()

  • 以Stream流的形式读取文件的所有行
/**
 * 以流的形式读取文件的所有行
 * 读取的的字节是以UTF-8解码的字符集
 * 
 * @param path 文件的路径
 * @return Stream<String> 文件中的行组成的流
 * @throws IOException 出现IO错误时抛出该异常
 * @throws SecurityException 如果是默认提供程序,则安全管理器是已安装,检查读取方法来检查对文件的读取访问
 */
public static Stream<String> lines(Path path) throws IOException {}

/**
 * 以流的形式读取文件的所有行
 * 		该方法和readAllLines不同,不会将所有行读取到一个List中,而是以流的形式进行惰性加载
 * 		以指定的解码的字符集读取字节,支持readAllLines的行终止符
 * 当该方法返回时,后续读取文件发生的IOException将会在读取Stream流的方法处抛出一个包装的UncheckedIOException.如果关闭文件发生IOException也会包装成为一个UncheckedIOException
 * 返回的流封装了一个读取器,如果需要周期性的读取文件,需要使用try-with-resources语句来保证stream的close方法被调用,从而关闭打开的文件 
 * 
 * @param path 文件的路径
 * @param cs 指定的解码格式
 * @return Stream<String> 文件中的行组成的流
 * @throws IOException 出现IO错误时抛出该异常
 * @throws SecurityException 如果是默认提供程序,则安全管理器是已安装,检查读取方法来检查对文件的读取访问
 */
public static Stream<String> lines(Path path, Charset cs) throws IOException {}

问题一

  • path对应的文件中读取所有内容,并按行分割,返回一个Stream. 从一个文件中读取连续的某几行内容:
try{
    return Files.lines(Paths.get(file)).skip(start).limit(limit).collect(Collectors.toList());
}catch(IOExceptione){
    logger.error("get content from {} error,{}", file, e.getMessage());
}
  • 当长时间运行后报错:
/proc/stat: Too many open files

原因

  • Files.lines() 这个方法,没有关闭打开的文件
  • 如果需要周期性的读取文件,需要使用try-with-resources语句来保证streamclose方法被调用,从而关闭打开的文件

解决

  • 使用try-with-resources改造业务方法:
try(Stream<String> stream = Files.lines(Paths.get(file))){
    return stream.skip(start).limit(limit).collect(Collectors.toList());

} catch (IOException e){
    logger.error("get content from{} error,{}",file, e.getMessage());
}

总结

  • try-with-resources语句可以自动调用资源的close方法.等价于如下方法:
Stream<String> stream = Files.lines(Paths.get(file));
try {
    return stream.skip(start).limit(limit).collect(Collectors.toList());
} catch (IOException e){
    logger.error("get content from{} error,{}",file, e.getMessage());
} finally {
    stream.close();
}
  • Stream.close(): 调用Stream.close() 方法会调用Stream流管道中所有的关闭处理程序
    • Stream.onClose()方法:
/**
 * 返回一个增加了额外的关闭处理器的等价的Stream流  
 * 当调用Stream.close(0方法被调用时,关闭处理器会按照被添加的顺序执行
 * 即使有关闭处理器抛出异常,所有的关闭处理器也都会执行
 * @param closeHandler 关闭处理器
 * @return 包含额外的关闭处理器的Stream流
 */
 S onClose(Runnable closeHandler);
  • 问题: Files.lines() 返回的Stream是否添加了close handler?
    • Files.lines() 方法在返回的Stream中添加了close handler, 可以在close handler中关闭打开的文件
    • 因此,必须调用Files.lines() 中的Stream.close() 方法来保证文件关闭
  • 问题: Stream中的collect(Collectors.toList()) 方法是结束操作,将Stream转化为List,Stream.collect() 中会调用close() 方法吗?
    • Stream.collect() 中没有调用close() 方法

问题二

  • 使用Files.lines() 方法导致的文件句柄泄漏,临时文件被删除后没有释放磁盘空间引起服务器服务磁盘被大量占用问题

原因

  • 在服务器磁盘告警占用80%, 登录服务器查看日志目录,发现日志目录占用磁盘空间不足1G, 判断是其余非日志目录占用了磁盘空间
  • 执行命令 [du -h -s * ] 检查主要目录,发现所有的目录占用空间远远小于使用命令 [df -h] 命令返回的磁盘总使用空间. 由此判断是文件句柄泄漏导致临时文件虽然被删除但是磁盘空间未释放引起的
  • 执行命令 [lsof | grep deleted] 列出所有已经打开并且已经删除的文件,返回大量临时文件
  • 重启Java进程,磁盘空间占有率大量下降,可以确定问题原因在于Java代码导致的文件句柄泄漏

分析

  • 通常情况下,导致文件句柄泄漏IO操作中文件读写类没有关闭导致的,所以重点检查相关IO操作相关代码,比如BufferedWriter,BufferedReader之类
  • 发现问题代码:
long total = java.nio.file.Files.lines(filePath).count();

解决

  • Files.lines() 方法也是文件读写操作,也需要进行关闭
  • 将问题代码修改如下:
long total = 0L;
try (Stream<String> stream = java.nio.file.Files.lines(filePath)) {
    total = stream.count();
}

总结

  • Files.lines()JDK8中的方法,能够更加简单地处理文本文件,源码如下:
 public static Stream<String> lines(Path path) throws IOException {
        return lines(path, StandardCharsets.UTF_8);
    }

    public static Stream<String> lines(Path path, Charset cs) throws IOException {
        BufferedReader br = Files.newBufferedReader(path, cs);
        try {
            // 添加asUncheckedRunnable到Stream的关闭回调
            // asUncheckedRunnable中关闭br
            return br.lines().onClose(asUncheckedRunnable(br));
        } catch (Error|RuntimeException e) {
            try {
                br.close();
            } catch (IOException ex) {
                try {
                    e.addSuppressed(ex);
                } catch (Throwable ignore) {}
            }
            throw e;
        }
    }

    private static Runnable asUncheckedRunnable(Closeable c) {
        return () -> {
            try {
                c.close();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        };
    }
  • Files.lines() 返回的Stream流要进行关闭操作,否则会导致文件句柄泄漏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮Chova

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值