File Descriptor是Linux下概念,fd 是 int类型非负数!
进程打开File,Socket,Pipe后生成一个File Descriptor,它是打开这个系统资源的标识符。
Linux每个进程fd最大1024个,超过之后进程 crash,crash堆栈如下:
- 使用匿名共享内存
E AndroidRuntime: java.lang.Error: java.io.IOException: SharedMemory_create failed: EMFILE (Too many open files) F DEBUG : Abort message: 'FORTIFY: FD_SET: file descriptor 1462 >= FD_SETSIZE 1024' |
-
使用fd = open(filename, O_RDONLY) 打开文件
返回fd变量场景如下:
- epoll初始化:fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC) / fd = epoll_create1(EPOLL_CLOEXEC)
- 打开文件:fd = open(filename, O_RDONLY)
- 共享内存:fd = getParcelFileDescriptor(new MemoryFile(
还有更多隐蔽的生成了fd,如:new Socket,new Thread,new PipedOutputStream‘
如何监控fd泄漏?
- StrictMode
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
- 脚本定时读取fd:本地写shell/python脚本定时读取proc/pid/fd
- shell命令读取fd总量或fd列表
lsof -n -p pid | wc -l
fd标识符和泄漏文件类型关系
类型 | fd标识符 |
网络请求 | Socket |
HandlerThread | anon_inode:[eventpoll]和anon_inode:[eventfd]成对出现 |
in/output打开文件 | /data/data/x,/data/app/x,/storage/emulate/0/x 例如 File(cacheDir, "file").createNewFile() |
打开数据库文件 | /dev/ashmem |
InputChannel泄露时增加明显 | anon_inode:[dmabuf] |
线程 | ? |
FD泄漏优化:
- HandlerThread用完后要关闭(HandlerThread::quitSafely()/HandlerThread::quit())
- 减少window数量:每增加1个window上涨10个fd
- 停止无用的线程:1个线程增加1个fd