结论
当使用 Files 工具包时, 需要额外注意返回值类型, 如果是 Stream
以及 DirectoryStream
这两个接口, 则需要在使用后及时调用其 close 方法释放资源, 推荐使用更优雅的 try-with-resources 结构
如果不执行 close 释放资源, 则打开的文件等资源将一直被占用, 直到 Java 进程终止. 当打开的资源足够多, 就有可能触发到 Linux 系统配置的进程打开文件上限, 进而导致 Java 进程功能异常, 报错 打开的文件过多
过程
首先是上传文件发现系统异常, 查日志发现是报 打开的文件过多, 通过 lsof -p 47090 | wc -l
发现 Java 进程打开了 4000+ 文件, 通过 ulimit -a
或 ulimit -n
查看系统配置的 Open Files 上限是 1024, 通过 lsof -p 47090
查看打开的文件列表, 发现里面有很多是上传文件的文件夹目录, 类似下面
...
java 47090 root 472r DIR 253,2 47 15032447518 /home/bid/upload/53b9bbe6e29e7eadeffa7c03962108c5
java 47090 root 473r DIR 253,2 47 15032447518 /home/bid/upload/53b9bbe6e29e7eadeffa7c03962108c5
java 47090 root 474r DIR 253,2 50 16643000312 /home/bid/upload/799e6138b45ac922d9c34a218a47c3a8
java 47090 root 475r DIR 253,2 47 1077111019 /home/bid/upload/c942c119927d83a3d9bb4e13a46adc75
java 47090 root 477r DIR 253,2 53 7160 /home/bid/upload/66a37cd0d1f4c76c9aaafcfbb82203d9
java 47090 root 478r DIR 253,2 47 1077111019 /home/bid/upload/c942c119927d83a3d9bb4e13a46adc75
java 47090 root 481r DIR 253,2 53 1611530376 /home/bid/upload/41a9b0ca26a194bf204ccf83ee199c67
java 47090 root 482r DIR 253,2 50 5368709550 /home/bid/upload/8ec135c1594ef233337b2a041a558c20
java 47090 root 485r DIR 253,2 50 5368709550 /home/bid/upload/8ec135c1594ef233337b2a041a558c20
...
重启 Java 进程后, 查看打开的文件数是 350 左右, 可正常上传文件, 但是随着上传文件接口的调用, 打开的文件数在不停上涨, 且对应的文件目录一直处于打开状态, 不能及时释放, 怀疑是流之类的没有及时关闭, 导致资源没有释放
检查代码, 发现没有使用到 InputStream / OutputStream 之类的 IO 流, 但是有使用到 Files.list
方法, 其返回值类型是 Stream<Path>
, 同样需要 close, 在加了 try-with-resources 处理后, 重启 Java 进程, 发现打开的文件数不再上涨, 维持在 350 左右, 且打开的文件列表里的目录类型也会很快被释放, 问题解决
查看 Files 工具类, 发现其中有好几个方法会返回 Stream<Path>
或 DirectoryStream<Path>
, 比如 find
/ lines
/ list
/ newDirectoryStream
/ walk
, 都需要做 try-with-resources 处理