博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android句柄泄漏(Fd leak)排查
阅读量:7145 次
发布时间:2019-06-29

本文共 3325 字,大约阅读时间需要 11 分钟。

背景

句柄泄漏(Fd leak)不是什么新鲜问题了,网上一搜这种问题,有很多种解决办法。 但没有系统性,导致排查问题效率极低。笔者希望通过这一篇文章,帮助大家理清思路,快速解决问题。

排查问题的黄金步骤

  1. 查log
  2. 找复现步骤
  3. 查代码

本文也将按照这几步,逐步深入到问题中。

查log

一般发生这种问题时,堆栈如下:

java.lang.RuntimeExceptionCould not read input channel file descriptors from parcel.android.view.InputChannel.nativeReadFromParcel(Native Method)android.view.InputChannel.readFromParcel(InputChannel.java:148)android.view.IWindowSession$Stub$Proxy.addToDisplay(IWindowSession.java:804)android.view.ViewRootImpl.setView(ViewRootImpl.java:770)android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3716)android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2901)android.app.ActivityThread.-wrap11(Unknown Source:0)android.app.ActivityThread$H.handleMessage(ActivityThread.java:1616)android.os.Handler.dispatchMessage(Handler.java:106)android.os.Looper.loop(Looper.java:173)android.app.ActivityThread.main(ActivityThread.java:6653)java.lang.reflect.Method.invoke(Native Method)com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)com.android.internal.os.ZygoteInit.main(ZygoteInit.java:821)android.view.InputChannel.nativeReadFromParcel(Native Method)复制代码

显然这种堆栈是没什么卵用的,唯一的用处就是,你把这log去网上一搜,大家说这可能是句柄泄漏(Fd leak)问题。

找复现步骤

这一步就有些学问了。 你有2种手段来找到这个问题的复现步骤:

  1. 利用“严格模式”,让app主动输出log,直接定位到发生问题的代码。
  2. 通过纯黑盒的方式,找到发生crash的操作步骤。

利用"严格模式(Strick Mode)"输出日志

严格模式的具体使用,大家可以移步官网:

public void onCreate() {     if (DEVELOPER_MODE) {         StrictMode.setThreadPolicy(new ThreadPolicy.Builder()                 .detectDiskReads()                 .detectDiskWrites()                 .detectNetwork()   // or .detectAll() for all detectable problems                 .penaltyLog()                 .build());         StrictMode.setVmPolicy(new VmPolicy.Builder()                 .detectLeakedSqlLiteObjects()                 .detectLeakedClosableObjects()                 .penaltyLog()                 .penaltyDeath()                 .build());     }     super.onCreate(); }复制代码

我们只要加入detectLeakedClosableObjects() penaltyLog()这两个方法,logcat里面搜索“Strick Mode”关键字,就能直接定位到是哪个Closable没有关闭。而往往大量的Closeable没有关闭,会导致句柄泄漏(Fd leak)的发生。

但是,严格模式不是那么的可靠,他并不能帮你发现所有的问题。我就经历过靠严格模式排查了一遍过后,问题依然存在的情况。最后还是靠找到复现步骤、人工查代码解决的。所以接下来的东西也很重要:

找到发生crash的操作步骤

找复现步骤,可以通过静置手机、跑monkey等等手段,“等待”crash的发生。但,这太没效率。本文希望通过以下步骤,缩短这个“等待”的时间

找到一部fd上限低的手机

我们知道导致句柄泄漏(Fd leak)的原因是:当前进程所持有的fd(file descriptor)数量,超过了操作系统所设置的上限。那我们首先要知道发生crash的手机对每个进程设置的fd上限是怎样的。

根据我的调研,华为mate10 fd上限是3万多,这种手机是不大容易复现问题的,上限太高。小米note1,华为p9,上限是1024,这类手机是比较容易复现问题的。另外,从发生crash时前后的logcat来看,有些机型,会在发生句柄泄漏(Fd leak)问题后,动态的调高fd上限。比如小米mix 6。

那怎么找呢?执行

adb shell ulimit -a (无需root)

你将会看到:

nofiles(descriptors)一项所示的数量,就是每个进程可持有的fd上限。

更快一些:在到达fd上限之前,就找到步骤

首先,你需要一部root的手机。 进入adb shell,su,以下命令,可以查看你的app进程所占用的fd数量:

lsof | grep <进程号> | wc -l

你每做一些操作(或者静置手机不动),就运行一下这个命令,对比前后几次fd数量的变化,可以大大缩小问题的范围。

by the way:

每次都运行命令笨不笨呢?有没有更好的办法呢?可以尝试使用linux中的watch命令。例如:

watch -n 2 'adb shell su lsof | grep <进程号> | wc -l'

不过如果直接adb shell su lsof的话,查出来的结果为0。为何adb shell su这命令直接运行,不能正确的给adb提权到root?我也没深究。有经验的朋友可以在评论区回复,笔者不胜感激。

查代码

在进行了上面的排查步骤后,查代码的范围也被大大的缩小了。你可以通过搜索各种“Stream”字段,来看他们有没有正确的关闭。或者,通过删代码这种“做减法”的方式,逐渐定位问题。笔者就是通过“做减法”的方式,定位到是公司内部的一个组件库的问题。

转载于:https://juejin.im/post/5cbd50f36fb9a032086dcc9f

你可能感兴趣的文章
Spring Boot 2.0将会增强Actuator端点的特性
查看>>
移动互联网下半场,iOS开发者如何“高薪”成长?
查看>>
访谈:Kotlin在Pinterest的逆势生长
查看>>
Oracle开源Fn,加入Serverless之争
查看>>
阿里百川技术分享:OneSDK与手机淘宝技术能力开放
查看>>
GCM 3.0采用类似方式向Android、iOS和Chrome发送消息
查看>>
Scala的设计目标——Martin Odersky访谈(二)
查看>>
上学还是坐牢?百年老校“监控”学生惹争议
查看>>
埃隆·马斯克:比特币拥有着“极为出色”的结构,而纸质货币终将消失
查看>>
Graphql 初尝试2 --第一个例子
查看>>
Vue学习笔记之一 - 入门
查看>>
新JEP将简化Java类型变异
查看>>
ASP.NET 2.2 Preview 1首次支持Java SignalR客户端
查看>>
RocketMQ 4.3正式发布,支持分布式事务
查看>>
微软自夸Edge浏览器的电源效率
查看>>
SGI STL值List
查看>>
webpack与video.js一同使用的一些坑
查看>>
Sidekiq 信号处理源码分析
查看>>
lr 学习
查看>>
Objective-C Tips
查看>>