Files.lines方法使用的相关问题
Files.lines()问题一原因解决总结
问题二原因分析解决总结
Files.lines()
以Stream流的形式读取文件的所有行
public static Stream
<String> lines(Path path
) throws IOException
{}
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()方法:
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 {
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流要进行关闭操作,否则会导致文件句柄泄漏