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

tech2026-02-28  4

Files.lines方法使用的相关问题

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语句来保证stream的close方法被调用,从而关闭打开的文件

解决

使用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流要进行关闭操作,否则会导致文件句柄泄漏
最新回复(0)