FileNotFoundException 就是 “文件不存在异常”吗?

本文探讨了当Java程序抛出FileNotFoundException时,问题并非总是由于文件不存在,而是可能因打开的文件句柄超过系统限制。通过实验分析,确认了问题是由程序占用过多文件句柄导致系统资源超出内核限制。解决方案包括优化代码,确保资源及时关闭,以及调整系统文件句柄限制。
摘要由CSDN通过智能技术生成

前言

  使用Java操作过文件的话,对FileNotFoundException肯定是不陌生的,碰到这个问题,我们第一反应就是给定的path文件不存在。但在某种极端条件下,造成这个问题的并不是文件不存在,而是程序运行时的资源使用不当导致(已看破的同学可以直接拖到结尾了)。

问题现象 

  在我的程序中有个场景是反编译apk并分析所有bundle的文件字节流,其中有大量的读文件操作,当同时解析多个apk文件的时候,就出现了FileNotFoundException,我们先来看下异常堆栈(已忽略非关键信息):

java.io.FileNotFoundException: /tmp/appOnDiet/..../WindVaneSDK$2.smali (Too many open files in system)
    at java.io.FileInputStream.open0(Native Method)
...
    at com.tmall.wireless.ycombinator.biz.aod.resourcegc.ResourceFileCollector.isRSmaili(ResourceFileCollector.java:73)
...
WARNING: RMI TCP Accept-1099: accept loop for ServerSocket[addr=0.0.0.0/0.0.0.0,localport=1099] throws
java.net.SocketException: Too many open files in system
    at java.net.PlainSocketImpl.socketAccept(Native Method)
...

  看到上面的堆栈,第一反应就是check一下/tmp/appOnDiet/…./WindVaneSDK$2.smali,然而文件存在!但是且慢,我们再看一眼日志会发现不仅文件打开失败,Socket流也失败,而且都指向Too many open files in system,这将是解决整个问题的突破口。

问题排查 

初步定位

  在ata搜索了一下Too many open files in system,并没有找到类似的解决方案(这篇文章有提到,但没后续)。在google中找思路的时候,发现linux在内核层面会限制文件的打开数量,这里文件不仅涵盖普通文件和目录,还包括了socket等网络流。查看当前系统的配置,可以使用sysctl和ulimit查看,比如在我的系统中(Mac PRO, iOS:10.10.5)可以拿到数据如下:

$ sysctl -a | grep maxfiles
kern.maxfiles: 12288
kern.maxfilesperproc: 10240
$ ulimit -n
256

  其中sysctl查询到的是内核级别的File Descriptors(暂且叫他文件句柄吧)限制,默认配置下我的系统支持最大12288个文件句柄,而单个进程在内核级别限制在10240个。ulimit限制的是用户通过shell命令启动的进程文件句柄数目,可参考ulimit manualulimit provides control over the resources available to the shell and to processes started by it, on systems that allow such control.
  结合“Too many open files in system”,问题可以初步定位在程序打开的文件过多,导致文件句柄数目超出了内核限制,不过具体是超越了哪一个限制?根本的触发原因是什么?初步怀疑是超越了“ulimit -n” 和 “kern.maxfilesperproc”,因为这两个值比较小,但这还需要结合后面的实验来进行分析。

实验分析

  假设是超越了ulimit限制,那么写一个循环连续打开文件257次,应该是要复现too many open files这个问题的,写一个很简单的测试方法,故意创建BufferedReader但不关闭,如下:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值