android FD_SET_chk问题定位
- 一、FD报错
- 二、问题定位
- 2.1 APM定位
- 2.2 adb定位
- 2.3. 代码获取FD数
- 三、FD优化
一、FD报错
App在运行中记录报错如下,FD_SET,这个问题大概是文件描述符(File Descriptor,简称FD)超过了最大限制数,说明App内可能存在FD泄漏。
二、问题定位
2.1 APM定位
由于App使用了火山APM监测,找到此对应崩溃信息中的Native信息,可以看到FD归类,已超过1024个(每个手机的可打开的最大FD不同)。而大部分集中在data中,data里存在大量创建文件没有关闭造成FD超过1024.
2.2 adb定位
// 1.先查询到App包名对应的pid
adb shell ps
// 2.cat /proc/pid/limits 查看最大可开启的文件数,找到open files (FD)、lock files (文件锁)
// 3. adb shell ls -l /proc/pid/fd,需要root权限
adb shell ls -l /proc/988/fd
2.3. 代码获取FD数
- 使用StrictMode框架定位具体代码占用fd,搜索日志TAG StrictMode 定位出问题的代码
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build()
)
- 直接代码获取fd数
private fun printFdInfo() {
val pid = android.os.Process.myPid()
val fdDir = File("/proc/$pid/fd")
if (fdDir.isDirectory) {
val files = fdDir.listFiles()
if (files != null) {
Log.d("FD_INFO", "进程 " + pid + " 当前打开的文件描述符数量: " + files.size)
for (file in files) {
try {
val filePath = file.canonicalPath
Log.d("FD_INFO", "文件描述符: " + file.name + " -> " + filePath)
} catch (e: IOException) {
Log.e("FD_INFO", "获取文件描述符信息失败", e)
}
}
}
} else {
Log.e("FD_INFO", "无法访问 /proc/$pid/fd 目录")
}
}
三、FD优化
- 数据库不要过多,数据库设计要精简合理,App退出时及时关闭
- 文件操作完毕后,要及时close
- 避免大量创建文件,使用缓存的文件进行操作
- Socket请求、Http请求尽量避免轮询