某天一个运行很久没有出问题的服务报警了,查看日志是如下错误,
赶紧排查,搜索“java.io.IOException: error=24, Too many open files”,找到类似的这篇文章(https://blog.youkuaiyun.com/imliuqun123/article/details/103925527),
通过“lsof -p Pid | wc -l” 查看了进程打开的文件总数,使用“ulimit -a”命令查看了系统的限制是多少。发现确实是超了。
通过“lsof -p Pid”命令查看了进程打开的文件的具体名称,发现很多文件后后面都标注了“(deleted)”,
根据文件名称大概定位了是哪部分代码,但具体是哪里出问题了还不确定,毕竟上线运行一段时间了。想着重启一下服务,新的进程就可以将没有用的文件关闭了,于是先重启了一下服务,问题解决,还是要排查出来到底是哪里出问题了呢??
以下是排查思路:
1、 怀疑是Java在Linux下执行命令的时候需要用到Process类,这个类在使用后需要destroy()掉,否者也会导致文件数开启的过多。
使用process.destroy()后问题并没有解决。
2、怀疑是 guava 读取文件 Files.asCharSource(file, Charset.defaultCharset()).readLines(new LineProcessor<String>() {}) 的问题,是这样用的(https://blog.youkuaiyun.com/bobozhangyx/article/details/80529079)
但是看源码里是有close的逻辑的,同时使用https://www.baeldung.com/java-read-lines-large-file这里面的FileUtils.lineIterator测试了一下,发现问题还是没有解决。
3、根据打开文件的作用排查看哪里用到了这些文件,发现LOGGER.info(Files.lines(Paths.get(new File(file).getPath())).count());这里有一个统计文件行数的逻辑。
这个逻辑最开始用wc -l实现的,后来改成了files的写法,结果留了一个坑。如果还是用这个方法,需要手动关闭一下stream。具体的看这里 https://qsli.github.io/2017/11/04/java8-file-stream/
try-with-resources语句:https://www.cnblogs.com/aspirant/p/8621848.html