记一次Binder内存不足导致的应用被杀

380 阅读3分钟

每个进程的可用Binder内存大小是 1M-8KB 也就是900多KB

事情的起因的QA压测过程发生进程号变更,怀疑APP被杀掉过,于是开始看日志(实际后来模拟的时候可以发现app确实被杀掉了)

  1. APP的压测平台会上报进程号变更时间点,发现是在临晨12:20分,先大概确定在哪个日志文件去找关键信息

  2. 一开始怀疑是crash,然后就在日志中过滤crash关键字,过滤出日志

image.png

  1. BroadcastQueue: Can't deliver broadcast to com.baidu.launcher (pid 951). Crashing it
  1. ProcessRecord: ProcessRecord.kill, reason = scheduleCrash for 'can't deliver broadcast' failed noisy = true killedByAm = false pid = 951 com.baidu.launcher

目前来看是由于广播发不出去导致系统kill了APP

于是先网上搜了下scheduleCrash看下是否有前人的智慧留存,运气不错,有相似情况,原因是Binder通信申请内存不时候内存不足导致的报错

cloud.tencent.com/developer/a…

于是下一步就过滤binder关键字

image.png

依然是看kill前后时间段的binder打印

JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 124)

有很多这个打印,这个是什么时候打印的呢,查了一下JavaBinder,发现没有这个类,网上搜了下

blog.csdn.net/xiaoxiaosun…

发现这个打印是在android_util_Binder.cpp,于是用Google提供的在线源码查看工具Android Code Search搜了下,发现打印位置

image.png

报这个错误的时候根据代码逻辑,有2个原因,一个是binder transaction失败而且传递数据大小超过限制 200*1024 也就是200KB, 还有一个就是AIDL接口服务挂了,那么现在就可以过滤下是哪个原因

TransactionTooLargeException

DeadObjectException

通过过滤这两个关键字,发现是第二个原因

image.png

可以看到BtGatt也在打印错误,这是BLE蓝牙的相关打印,结果压测场景是按住遥控器说话(这个过程会通过遥控器收音,发送语音数据给设备),所以确定是这个操作引发。这个内存不足的猜想,要么是每次传递数据过大,要么就段时间传递的太多了,导致这时候广播通知,跨进程发现共享内存不足,kill APP,通过Log发现parcel size = 160字节,应该不是太大导致,于是开始查看传递次数,发现出问题这次,收音持续了10分钟多,远超过脚本设定的每20S,收音2S的情况。但是也考虑到这种异常情况和binder传递数据的限制,决定加上了收音时间限制和数据积攒一定数量再发送一次的操作,降低跨进程频率。

如果怀疑是这个原因,可以直接过滤parcel字段,parcel是跨进程读写的中间人,系统在写入出问题的时候会带上这个字段的打印

后来去模拟复现这个问题,创建1个Activity,1个Serivice(设置process:remote)执行在其他进程,通过AIDL的方式跨进程,开启2个线程循环向写入Service写入字节数组,发现出错的打印基本一致,于是就确定了之前的猜测。