应用句柄泄露分析实例

来源:互联网 发布:白噪音app 知乎 编辑:程序博客网 时间:2024/05/24 04:16

前两天项目紧张阶段报出来一个Monkey测试出现的句柄泄露问题

当然这类问题日志和抛出的异常肯定不是直接告诉你句柄泄露啦、哪里泄露啦,测试报出来的问题是

AndroidRuntime: java.lang.RuntimeException: Could not read input channel file descriptors from parcel.

不少人都见过吧,其实这个也差不多就是告诉你文件句柄有了问题,可能还会怀疑是不是系统有问题导致的,好吧,还需要其他佐证来分析。

继续在这个日志附近搜索,哟,发现了宝贝了。

还有两句比较明显的,基本上可以确认就是句柄泄露了。

Parcel  : dup() failed in Parcel::read, i is 0, fds[i] is -1, fd_count is 2, error: Too many open files 

08-30 05:52:02.633  5258  5258 E InputChannel-JNI: Error 24 dup channel fd 1011.

开始解把

先搜索Cursor关键字,毕竟cursor也是要占用句柄的,代码里面都有加close操作,排除

然后搜索关键字File、InputStream和OutStream看看是否有未关闭的,没有问题排除

还是没找到问题,只能祭出杀手锏了,根据日志显示的操作简单操作几次看看前后文件句柄变化主要是哪几个方面

shell命令adb shell ls -al /proc/pid/fd

进入应用就已经有四十好几个了,大部分都不知道是什么,猜也没什么意义,还是直接操作几下看看差别吧。

lrwx------ u0_a67 u0_a67 2016-09-02 16:32 0 -> /dev/null
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 1 -> /dev/null
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 10 -> /system/framework/core-libart.jar
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 11 -> /dev/binder
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 12 -> anon_inode:inotify
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 13 -> /dev/urandom
l-wx------ u0_a67 u0_a67 2016-09-02 16:32 14 -> /dev/cpuctl/tasks
l-wx------ u0_a67 u0_a67 2016-09-02 16:32 15 -> /dev/cpuctl/bg_non_interactive/tasks
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 16 -> pipe:[63424]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 17 -> socket:[63423]
l-wx------ u0_a67 u0_a67 2016-09-02 16:32 18 -> pipe:[63424]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 19 -> anon_inode:[eventfd]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 2 -> /dev/null
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 20 -> anon_inode:[eventpoll]
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 21 -> /system/app/***/***.apk
lrwx------ u0_a67 u0_a67 2016-09-02 16:31 22 -> /data/data/*****/databases/**.db
lrwx------ u0_a67 u0_a67 2016-09-02 16:31 23 -> socket:[64032]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 24 -> anon_inode:[eventfd]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 25 -> anon_inode:[eventpoll]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 26 -> /dev/kgsl-3d0
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 27 -> /dev/ion
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 28 -> socket:[65690]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 29 -> socket:[63435]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 3 -> socket:[18693]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 30 -> anon_inode:[eventfd]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 31 -> socket:[57063]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 32 -> anon_inode:dmabuf
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 33 -> anon_inode:dmabuf
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 34 -> /dev/ion
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 35 -> anon_inode:dmabuf
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 36 -> anon_inode:dmabuf
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 37 -> anon_inode:dmabuf
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 38 -> anon_inode:dmabuf
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 39 -> anon_inode:[eventpoll]
l-wx------ u0_a67 u0_a67 2016-09-02 16:32 4 -> /sys/kernel/debug/tracing/trace_marker
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 40 -> anon_inode:dmabuf
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 41 -> anon_inode:dmabuf
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 42 -> socket:[64082]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 43 -> socket:[65138]
lrwx------ u0_a67 u0_a67 2016-09-02 16:32 46 -> socket:[65760]
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 5 -> /system/framework/framework-res.apk
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 7 -> /system/media/theme/default/icons
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 8 -> /dev/ion
lr-x------ u0_a67 u0_a67 2016-09-02 16:32 9 -> /dev/__properties__root@****:/ #
果不其然,连续多次网络操作之后,可以看到socket和dmabuf明显增多了,dmabuf这个应该是系统和驱动那边的,不需要考虑。但是这里的socket显著增长应该是有问题的,socket这里估计应该有两类一个是本地的进程间通信用的,还有一类应该就是重点网络操作。

怀疑网络访问的socket没有关闭,本应用中用的是apache的DefaultHttpClient。

难道每次访问都新建一个Client会造成socket无法回收?先改成单例,然并卵

DefaultHttpClient有主动关闭连接或者socket的接口?有一个getConnectionManager().shutdown();,加上试试依然没卵用

继续修改成OkHttp和HttpUrlConnection方式依然没改善,这就奇了怪了,这些API肯定不会有问题的,难道是哪个对象一直持有了这些东西么

此时另外一个同事提醒曾经有同事用HandlerThread没有退出导致内存泄露,好吧,难道要我检查一遍我的内存泄露问题吗,应该不会是那个问题,毕竟从日志上的GC信息来看,配置的192M的堆大小才用了不到50M内存问题应该比较轻微。

不过我这边的网络访问确实是有线程哦,虽然设计之初我的想法是只存在一个线程操作特意设置的队列,还是确认一下吧,于是再次shell

ps -t | grep pid

u0_a67    6302  885   1026844 62608 SyS_epoll_ 00f74ea3ac S ****************
u0_a67    6307  6302  1026844 62608 do_sigtime 00f74bd9f8 S Signal Catcher
u0_a67    6308  6302  1026844 62608 poll_sched 00f74ea59c S JDWP
u0_a67    6309  6302  1026844 62608 futex_wait 00f74bd9f8 S ReferenceQueueD
u0_a67    6310  6302  1026844 62608 futex_wait 00f74bd9f8 S FinalizerDaemon
u0_a67    6311  6302  1026844 62608 futex_wait 00f74bd9f8 S FinalizerWatchd
u0_a67    6312  6302  1026844 62608 futex_wait 00f74bd9f8 S HeapTaskDaemon
u0_a67    6313  6302  1026844 62608 binder_thr 00f74ea4d8 S Binder_1
u0_a67    6314  6302  1026844 62608 binder_thr 00f74ea4d8 S Binder_2
u0_a67    6319  6302  1026844 62608 inotify_re 00f74eb388 S FileObserver
u0_a67    6321  6302  1026844 62608 futex_wait 00f74bd9f8 S pool-1-thread-1
u0_a67    6324  6302  1026844 62608 SyS_epoll_ 00f74ea3ac S RenderThread
u0_a67    6326  6302  1026844 62608 binder_thr 00f74ea4d8 S Binder_3
u0_a67    6328  6302  1026844 62608 futex_wait 00f74bd9f8 S ************
u0_a67    6331  6302  1026844 62608 futex_wait 00f74bd9f8 S hwuiTask1
u0_a67    6335  6302  1026844 62608 SyS_epoll_ 00f74ea3ac S WifiManager
u0_a67    6363  6302  1026844 62608 unix_strea 00f74eb464 S TcmReceiver
u0_a67    12116 6302  1026844 62608 futex_wait 00f74bd9f8 S ****************
u0_a67    12162 6302  1026844 62608 futex_wait 00f74bd9f8 S Thread-177
u0_a67    12163 6302  1026844 62608 futex_wait 00f74bd9f8 S pool-2-thread-1
u0_a67    12258 6302  1026844 62608 futex_wait 00f74bd9f8 S Thread-181

惨了,貌似还真有线程增多的情况出现

u0_a67    13068 6302  1055972 79580 binder_thr 00f74ea4d8 S Binder_6
u0_a67    14500 6302  1055972 79580 futex_wait 00f74bd9f8 S Thread-191
u0_a67    14518 6302  1055972 79580 futex_wait 00f74bd9f8 S Thread-195
u0_a67    14539 6302  1055972 79580 futex_wait 00f74bd9f8 S Thread-200
u0_a67    14563 6302  1055972 79580 futex_wait 00f74bd9f8 S Thread-205
u0_a67    14582 6302  1055972 79580 futex_wait 00f74bd9f8 S Thread-210
u0_a67    14605 6302  1055972 79580 futex_wait 00f74bd9f8 S Thread-215

这个匿名的非线程池的线程越来越多,啊,难道我的那个线程真是出问题了。这里的线程都没有命名导致不能直接定位是哪个线程出了问题,以后大家用到线程的时候最好自己命名一下能很好的定位问题,这里我的操作很明确所以很容易就找到了是访问网络的那个线程。

线程里面有一个BlockingQueue队列,本来我是想利用interrupt来结束这个进程的,但是但是但是,当时脑残居然调用的是isInterrupted()方法,心中一万只草泥马奔腾而过。

本次问题比较低级就不好意思贴代码了,就写一下这些分析的一点点思路吧。


正好今天又帮同事看了一个Monkey测试的内存溢出问题,在下篇也给大家分享一下吧,也给自己留着。

2 0
原创粉丝点击