句柄泄漏和Handler的底层机制

【简介】

   之前在做一个无埋点SDK相关开发的时候,由于上报逻辑比较复杂,故而想到了用hander队列的形式处理事件的上报,但是SDK上线之后,发现出了一个句柄泄漏的bug,百思不得其解,后来看了Handler的底层源码,又做了一些句柄数的追踪和分析,解决了这个问题。

    一、Handler在java层的机制

    如下图,handler的应用层机制很简单,不同的线程通过handler,发送message到messageQueue里面,对应的Looper开启一个死循环,然后一直轮询,如果队列里有待处理的消息,就处理消息;如果没有消息,则开始休眠,节约资源。个中细节就不在赘述,如有需要,可以自行搜索相关资料。

Handler的应用层机制


    二、Handler的native层机制

    我们首先来看Looper.loop()的源码

Looper.loop()源码

    我们发现,在这个死循环里,它调用了MessageQueue下的next方法,那么我们再看看这个next方法干了些什么:

MessageQueue的next()方法

    关键方法是这个nativePollOnce():当运行到这里时,会调用native层的方法,系统去轮询一次,如果MessageQueue当前没有要处理的Message,则线程休眠,不占用资源,这里为什么休眠不会产生ANR呢,我们后面会分析。

    我们现在注意一下nextPollTimeoutMillis这个变量,这个变量代表MessageQueue下次被唤醒的时间。我们知道,MessageQueue里Message在加入队列的时候,会按照执行的时间顺序排列;每次消息入队列时,MessageQueue都会尽量计算出一个精确的时间,假如这个时间是计算出来是2000ms,此时消息队列中没有消息需要马上处理时,会判断用户是否设置了Idle Handler,如果有的话,则会尝试处理mIdleHandlers中所记录的所有Idle Handler,此时会逐个调用这些Idle Handler的queueIdle()成员函数,只会会再次调用nativePollOnce()方法,线程阻塞住,不占用资源。当时间到了,会往管道流中写入字节流,唤醒线程,处理Message。

    我们知道,安卓的底层是Linux系统。当Looper休眠时,用的是底层的epoll机制来完成阻塞动作,故而不会产生ANR。

    源码的唤醒调用如图:

MessageQueue.cpp的nativeWake()

    最终调用了Looper.cpp源码的wake()方法

Lopper.cpp的wake()方法

    我们可以看到,唤醒只是往管道流里写了一个"w"的字符流。所以唤醒机制,我们可以直观的理解为:

唤醒机制

    在Linux底层,每个线程所能操作的句柄上限是1024个,一旦超过了这个值,则会报句柄泄漏的错误,导致崩溃。

    所以,是不是我们的上报无埋点的数据时,频繁的休眠唤醒导致句柄数超过了上限呢?


    三、查看线程的句柄数

    我们需要一个root了的手机,如果手头没有能用的测试机,可以使用模拟器。

    我用了一个低版本的模拟器(5.0版本),因为高版本的模拟器也不好直接获取root。

    我们先要获取进程的id,这个可以通过AS的Logcat查看。

    之后通过adb shell进入模拟器,cd到/proc/进程id/fd文件夹下,然后ls -al,就可以在Logcat中打印出当前线程所消耗的句柄。如果你没有root权限,fd文件夹是访问不了的。

    笔者在启动APP后,正常使用了一下APP,接着打印出了当前程序占用的句柄数,发现有800多个句柄开销,大多数是数据库所持有的,搞了半天原来是数据库的问题。但是为了防止意外,笔者已经把SDK中的所有Handler替换掉了。


    四、总结

    在android开发中,我们的这些操作会消耗句柄数:

    1.数据库的读写,不关及时关流会导致句柄开销增大;

    2.文件流的读写,如果操作不好,也容易导致句柄消耗过大;

    3.Handler频繁唤醒等。

    一旦出现句柄泄漏的问题,核心思想就去排查流的读写有没有出问题。因为在安卓底层,Linux对于句柄的操作很多都是通过流来体现的,如果句柄数超过了上限,肯定会出问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容