打开太多的文件,too many open files (java)

最近线上Java项目要导入100多万条记录。每3天都会报‘打开太多的文件‘错误。ulimit -a命令查看,发现还是默认值,一直以为是这块默认值没有改的问题。叫领导改下,领导说是程序问题。所以每三天就要重启下,然后就可以再导入三天了。最近借助文心一言研究了下。

文心一言说这个错误通常意味着程序试图打开的文件数量超出了操作系统所允许的限制。这个限制是由操作系统的文件描述符(file descriptor)限制设置的。每个打开的文件、网络连接、套接字等都需要一个文件描述符。怪不得每次报这个错误的时候,redis都连接不上。

遂接着问文心一言,怎么查看进程打开了多少个文件描述符。在文心一言的众多方法中选择了lsof命令,不是系统自带的,还要安装。用命令‘lsof -p 端口‘查看了下,竟然打开了3851个文件描述符。好多前天的文件描述符没有释放,线程早都结束了,为什么不释放了,很奇怪。

遂接着问文心一言,JVM垃圾回收为什么没有释放文件描述符。得到的答案是:文件描述符并不属于JVM的内存管理范畴,因此垃圾回收机制不会直接释放文件描述符。文件描述符是操作系统层面上的概念,用于标识打开的文件、网络连接或其他I/O资源。在Java程序中,当你打开一个文件、创建一个网络连接或执行其他需要I/O操作的任务时,操作系统会为你分配一个或多个文件描述符。这些文件描述符的管理和释放是由操作系统和Java的本地方法库(如JNI)共同完成的,而不是由JVM的垃圾回收器负责。看来只能手动close了。

根据‘lsof -p 端口‘查询未释放的文件描述符,找到对应操作文件的代码,的确一个上传图片到云没有关闭流,还有一个查询本地数据库DB文件的方法,没有释放数据库连接和statement,还有一个目录描述符没有释放,但是我目录在finally里面都删除了啊,文件描述符也说这个目录是删除的,但就是没释放。因为操作这个目录的方法是java.nio.files里面的list方法,我想这个方法总不能有问题吧。目录没释放肯定是因为目录里面的文件没有释放造成的。

第二天更新后,发现没有未释放的图片和DB文件描述符了,但是目录描述符还没有释放。随着我导入文件越来越多,目录描述符也越来越多,估计3天后,又是too many open files错误了,我研究了下源码方法,用到了DirectoryStream<Path> 但是在异常块里面关闭的,没有在finally里面关闭。

遂想着重写这个方法吧,类竟然是final的。突然看到return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false).onClose(asUncheckedRunnable(ds));末尾竟然有onClose()方法,竟然有怎么没关闭了,奇怪了,看了释,这个是个handler了,要调用list(Path dir)方法的返回值Stream<Path>的close方法,才会触发handler.我恰恰是没有调用,我调用list(Path dir)的时候。直接Files.list(Paths.get(folder)).forEach()。返回值Stream<Path>都没有变量保存。肯定也没有关闭,随保存了返回值,然后finally关闭了。再导入就没有未释放的文件描述符了。

自学Java1(保姆级教学)——安装JDK

工欲善其事,必先利其器。不管你想学什么,第一步必须都是安装好相应的环境与工具,而大多数同学想学编程的话,必然都是为了找工作,目前国内单从就业情况来看的话,Java依旧是一骑绝尘。但是呢,什么语言其实都不重要,不同语言虽然在语法上肯定多多少少都有些出入,但是编程的思想其实都是一样的,这里呢我就不引战了,每个语言都有自身的优劣,只是我个人推荐,纯属一家之言:如果你是以学习为目的的话,是个大学生,暂时还没有工作就业的压力,C语言必然是最好的选择,大多数语言的底层也都是C语言写的;但是如果是以工作为目的的话,Java肯定是最把稳的一种语言,其完整的生态链是大多数语言无法比拟的,尤其是spring全家桶实在太过于强大,所以短期内,不要扯什么go语言能超过Java的大话,起码暂时还差很远。这里我再说一个自己的观点,就是国内,尤其是非一线大城市的小伙伴,最好不要去碰python,它在国内的就业市场并没有培训机构说的那般好,你在学之前,最好去搜一下在你所在城市的岗位是如何再做决定,岗位很多还可以学学看,如果岗位不多的话,建议还是不要太过冒险的好。

那么闲话不多说了,我们还是回归正题,由于Java已经被甲骨文收购,所以如果你想登录官网的话,应该是去www.oracle.com,那么登录官网之后,你可以看到在product选项中的Java:

进入之后,找到Java下载处:

在进入之后你会看到:

就在2021年9月14日,JDK17的版本正式发布,如果你是作为学习来用的话,其实就可以直接下载17了,因为我觉得学习踩踩坑其实也蛮好的,但是建议还是下载8和11,目前就我工作接触而言,基本都还是在用8,至于为什么,无外乎项目的维护,很多项目就是用老版本写的,如果贸然换成新版本的环境,可能会出现奇奇怪怪的问题,所以现在大多数公司还是都在用最经典的8,但是版本问题都不是我们学习的什么大问题,随意下载一个JDK的版本都可以,无所谓的,到时候公司让你用哪个你就再下载就是洛。你瞅瞅多少个版本的JDK:

什么???你不知道怎么找其他的JDK版本在哪???实在懒得找,你就关注点赞加评论,私信我给你私发总可以了吧,够保姆了吧。

下载好压缩包之后,你就直接安装就可以了,下一步下一步总会吧,这里我就不贴图了,如果你是默认的下一步到结束,应该和我的差不多:

那么如何检验自己的电脑是否装好了Java环境呢?很简单,搜索栏处输入cmd,或者win+R处输入cmd,打开命令提示符,输入 java -version:

如果安装成功的话,你就会看到你所安装的JDK版本。如果不可以的话,说明你的环境安装失败。

安装好java环境之后,就需要安装环境变量了,至于为什么需要安装环境变量,篇幅限制就不多做解释了,如果有小伙伴想知道的话,希望大家可以多多点赞和评论,如果人多的话,我也可以单独做一期视频来解释一下,讲一下为什么需要配置环境变量,如果我们不配置的话难道就不能进行开发了吗?当然,答案很明显不是的。

那么如何配置环境变量呢?以win10系统为例,右键此电脑,然后属性>高级设置>高级>环境变量>系统变量,在环境变量中新建一个变量项目,变量名你自己定,但是不要出现中文,可以参考我的:先新建一个环境变量名,命名为JAVA_HOME,然后将你JDK的路径输入进入,

然后再打开系统变量中的path:

配置环境变量的整个过程,也不过就是为了让计算机考研找到JDK中的bin目录,至于为什么要找到bin目录,是因为编译文件放在bin目录中,如果你再问我什么叫做编译文件,那我只能让你看看我前面已经发表过的文章了:相信看完的你会稍微明白一点。

而为什么之前要先配置一个JAVA_HOME,这样做是为了你以后如果想换一个版本的JDK的时候方便更改,开始如果实在听不懂也没关系,就先照着做,后面自然会懂。

当然了,你可能又会好奇了,这个JDK中的bin目录,lib目录,jre目录等等都是什么东西?里面包含了什么东西?有什么作用?对于这些,我只能说,对于入门的小伙伴来说,先对着做更重要一些,很多东西需要一定的基础去做铺垫才能解释明白,否则说了也无法是浪费大家的时间。

那么至此,安装Java环境就算是安装结束,大家的第一步也就算迈出去了,希望感兴趣的小伙伴可以关注点赞评论一下,更文实在不易,也希望大家可以给我一点点更下去的动力,让我知道确实有很多的小伙伴正在看我的文章,谢谢大家,也希望我会和大家一起进步,加油!

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。