你的位置:首页 > Java教程

[Java教程]Too many open files解决方案及原理


以下是我解决Too many open files异常时学习的知识的理解和总结,如有不正确指出,敬请指出!

  此问题中文搜索雷同,你可以尝试以下关键字:"file descriptor leak" "stackoverflow" "how to solve open files exception“等。

一下是我的一些总结,或许对您有所帮助!

1.fd
fd is short for file descriptor
在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件,
应用程序就是通过fd识别该文件/设备/服务..
你可能需要自行了解更多fd的定义及功能知识。

2.lsof
linux下的命令, 全称:list system open files
第1节说linux所有资源都是以file形式的,所以这个lsof命令是我们查看系统资源占用情况的得力工具。排查linux下各种资源耗尽,异常
等问题都可以使用它。中文能找到的几乎都是这种http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316599.html,以下为引用:

      在终端下输入lsof即可显示系统打开的文件,因为 lsof 需要访问核心内存和各种文件,所以必须以 root 用户的身份运行它才能够充
      地发挥其功能。直接输入lsof部分输出为:

          COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
          init 1 root cwd DIR 8,1 4096 2 /
          init 1 root rtd DIR 8,1 4096 2 /
          init 1 root txt REG 8,1 150584 654127 /sbin/init
          udevd 415 root 0u CHR 1,3 0t0 6254 /dev/null
          udevd 415 root 1u CHR 1,3 0t0 6254 /dev/null
          udevd 415 root 2u CHR 1,3 0t0 6254 /dev/null
          udevd 690 root mem REG 8,1 51736 302589 /lib/x86_64-linux-gnu/libnss_files-2.13.so
          syslogd 1246 syslog 2w REG 8,1 10187 245418 /var/log/auth.log
          syslogd 1246 syslog 3w REG 8,1 10118 245342 /var/log/syslog
          dd 1271 root 0r REG 0,3 0 4026532038 /proc/kmsg
          dd 1271 root 1w FIFO 0,15 0t0 409 /run/klogd/kmsg
          dd 1271 root 2u CHR 1,3 0t0 6254 /dev/null

          每行显示一个打开的文件,若不指定条件默认将显示所有进程打开的所有文件。
          lsof输出各列信息的意义如下:
          COMMAND:进程的名称 PID:进程标识符
          USER:进程所有者
          FD:文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等 TYPE:文件类型,如DIR、REG等
          DEVICE:指定磁盘的名称
          SIZE:文件的大小
          NODE:索引节点(文件在磁盘上的标识)
          NAME:打开文件的确切名称

你可能需要自行了解lsof命令使用细节

 

3. fix too many open files excepiton(POSIX)

  a. 网上尤其使用baidu搜出来的答案几乎全是“ulimit -n”查看最大能打开的fd的限制值,通常是 1024(意思是最多能打开1024个),然后
     使用“ulimit -n 4096”调大该限制。这里先下个结论,这个解决方案是碰运气式的,详情且看下面分解。

  b. too many open files 出现的原因:

      》第1节有说,linux(POSIX)所有存在都是以file形式表示的,所以引起这个异常的原因(几乎)就是你打开了太多‘files’,超过了限制。
         那a中调大限制的方法明显可以用啊。下面这段话给出了不推荐这样做的理由:

            摘自http://oroboro.com/file-handle-leaks-server/
            Wrong Answers, Myths and Bad Ideas
            Raise the file handle limit
               One common answer to this problem is to just raise the limit of open file handles and then restart the server every
           day or every few hours.
               This will delay the problem but likely will not fix it. It is possible that your program is not leaking and has a
           legitimate need to hold a large number of file handles. But if your program is designed correctly there usually isn’t
           a need to keep a large number of handles open – even if you have thousands of simultaneous connections. We’ll discuss
           some methods of managing that later.
                If this was a good idea the operating system would already come configured with a higher file descriptor limit. If
           this was necessary, Apache would require you to up this limit before running.

      》原因(大多数时候) 

               The problem is almost certainly that you are leaking file handles. That is, handles are being opened, and after you are
           done with them they are not closed.

                Leaked file handles can come from many sources, not just open files. Some common sources are:Sockets,Pipes,Database
           connections,Windows HANDLES,Files.

  c. 如何排查及修复(重点)

      》当不满足于“ulimit -n 4096”的解决方案,深入想要分析原因时,搜索到的分析方法也大同小异,大多和lsof命令相关,以下罗列一些;

          =.To find out PID for mysqld process, enter: pidof mysqld #pidof命令是找出进程的id号,如pidof java找到Java进程的id号
          =.List File Opened By a PID:lsof -p ${pid} #-p 参数是 --pid的意思, 如 lsof -p 10086是打印10086进程的所有open files
              或者 ls /proc/${pid}/fd #和上行一样,查看该进程打开的files
          =.List File Descriptors in Kernel Memory
              sysctl fs.file-nr #结果:fs.file-nr = 2688 0 379264
          => The number of allocated file handles
          => The number of unused-but-allocated file handles
          => The system-wide maximum number of file handles
                 sysctl fs.file-max #结果是能打开的最大files数量
          =.查看某个用户下打开的files: lsof -u jboss
          =.和计数器结合起来,计算打开的文件数量,如:lsof -p 10086|wc -l, ls -alt /etc/10086/fd|wc -l等等。
        这些分析方法大多是利用lsof配合参数和管道命令,或者是统计/proc/${pid}/fd目录,来分析你的目标的openfiles情况。

     》困惑

          =. lsof -u root |wc -l 结果是2223, 而ulimit -n 结果是1024, 为什么root用户当前的open files还要比limit大? 这个如果不清楚那
              分析就没意义了,因为你就是要解决open file limit问题的,结果root当前运行时就打开了比limit还多的files.
          =. lsof -p 54552|wc -l 结果是658,54552是我java的进程pid, 而ll /proc/54552/fd |wc -l,结果却是358,为什么统计同一个进程当前
              的open files数量却有如此大的差异? 以哪个为准,和ulimit -n的值有有什么关系?
          =. 先解释第2点,lsof will also give you memory mapped .so-files - which technically isn't the same as a file handle the
               application has control over. /proc/<pid>/fd is the measuring point for open file descriptors。 意思是说lsof的结果包含
               memory mapped .so-files,这些在原理上并不是一般的应用程序控制的fd。 而/proc/<pid>/fd目录很好的反映了fd的open情况。
               适当修改下lsof: lsof -p <pid> | grep -v mem | egrep -v '^COMMAND PID' | wc -l,这样就等同于/proc/<pid>/fd下统计情况。
          =. 是第2点的问题引起了第1点?使用lsof -u root |grep -v mem | egrep -v '^COMMAND PID' | wc -l, 结果是 1221,还是大于limit.
              就是说root用户当前open 1221 files是确定定, root 的 files limit是1024也是确定的, 而造成more than limit究竟为何:其实是
              limit这个限制针对的对象,最终找出是这样的:because the limit is on a per-process base and not per-user 。 limit限制是基于
              一个进程而言的(该用户拥有的进程)并非用户。 就是ulimit -n是1024的意思是由root用户执行的某个进程最多只能打开1024个文件,
              并非root总共只能打开1024个。
          =. sysctl fs.file-max 我的结果是379264,这个数字则是kenel内核总共能支持的open files数量。这个是不能改变的。

     》解决

          =. 至此可以解释为啥放大limit是碰运气式的,毕竟一个process正常情况下同时要打开超过1024个files还是比较少见的。
          =. 如果需分析leak或者查看open fiels的细节, 则应从pid粒度着手,而不要被user迷惑。
                 lsof -p <pid> | grep -v mem | egrep -v '^COMMAND PID'
                 或者对/proc/<pid>/fd 目录分析。
          =. uprize limit
               即使需要扩大limit限制,实际上大多是os上简单的“ulimit -n 4096”是行不通的,操作系统不允许。 以下两种方式供参考
                 Raising the Global Limit。Edit /etc/sysctl.conf and add the following line:fs.file-max = 65536
                                                     Apply the changes with:sudo sysctl -p /etc/sysctl.conf
                 Raising the per-User Limit.
                     &Edit as root the following system configuration file: % sudo vi /etc/security/limits.conf
                     &Modify the values for nuxeo user (we assume here JBOSS is launched with the sytem user "nuxeo")
                               nuxeo soft nofile 4096
                               nuxeo hard nofile 8192
                        If you want to raise the limits for all users you can do instead:
                               * soft nofile 4096
                               * hard nofile 8192
                     &Edit /etc/pam.d/su: sudo vi /etc/pam.d/su
                     &Uncomment the line:
                            session required pam_limits.so
                     &Once you save file, you may need to logout and login again

    d.总结

       扩大open files数量限制的操作可以起效,但在此之前相比你应该也对为何出错会感兴趣吧,不妨先分析一下喽。    

       以上总结中的观点是网上各处搜罗及个人理解所得,如有误,请谅解指正。