【故障排查:JDK8中Files.lines方法错误使用导致的Linux服务器文件描述符泄漏问题】
JDK8中Files.lines方法错误使用导致的Linux服务器文件描述符泄漏问题
-
- 1.1 需求背景
- 1.2 故障复盘-未及时发现原因分析
-
- 1. 2.1 历史遗留或疏忽
- 1.2.2 追求代码简洁性
- 1.2.3 测试环境不易发现问题
- 1.3 故障分析过程
- 1.4 修复方案
1.1 需求背景
做某个定制需求的过程中,为了知道第二个策略的数据文件的表头的内容需要读取第一个策略的数据文件第一行的内容。
当读取的文件比较大的时候,性能会有较大损耗,为了使用高性能的处理方式,尝试使用了java JDK8中的stream 流api。
public static String readFirstLine(String filePath) {Path path = Paths.get(filePath);try {return Files.lines(path).findFirst().orElse(null);} catch (Exception e) {log.warn("读取文件失败,filePath:{},错误信息:{},异常详情:",filePath,e.getMessage(),e);return null;}}
上面代码出现的问题:
- ⚠️ 资源泄漏(严重问题)
- Files.lines(path) 返回一个 Stream,这个流必须显式关闭,因为它背后打开了文件资源。
- 使用
try-with-resources
是正确做法。- 当前代码:流没有关闭,可能导致文件句柄泄漏。
1.2 故障复盘-未及时发现原因分析
1. 2.1 历史遗留或疏忽
- 早期 Java 版本中 Files.lines() 的资源管理不够明确
- 没有意识到需要显式关闭 Stream
- 代码审查时遗漏了这个细节
- 对 Stream API 的误解
误认为:
- Stream 会自动关闭(实际上不会(br.lines().onClose(asUncheckedRunnable(br)))
- 终端操作(如 findFirst())会关闭流(实际上不会)
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) {