Androdid中ExecutorService内存泄露原因分析
来源:互联网 发布:苹果mac爱奇艺视频 编辑:程序博客网 时间:2024/06/05 14:41
Androdid中ExecutorService内存泄露原因分析
我们都知道ExecutorService开启线程用于实现异步逻辑,ExecutorService维护一个线程池,但是因为我们大多数时候使用ExecutorService创建的线程的生命周期跟应用app的进程是一致的,所以没有造成任何的内存泄露的问题,但是实际上ExecutorService的使用如果不小心会给我们造成麻烦。所以本文对ExecutorService使用造成的内存泄露做了源码的分析(即分析线程池内部的线程为什么处于活着的状态在runnable执行完成之后)。
假定使用ExecutorService的代码如下:
public static void testExecutorService() { ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.execute(new Runnable() { @Override public void run() { //do something } });}
函数内部实例化局部变量,通常按照我们的理解是在函数执行完成,出了executorService变量的作用域,就会被回收内存(假定runnable已经执行结束),但是实际上结果不是这样子的,因为executorService变量创建的线程池中的线程还处于活着的状态,通常我们创建一个Thread并执行runnable的时候,在runnable执行结束的时候,thread会处于die的状态,并内存得到回收,接下去分析下为什么线程会处于活着的状态。首先我们查看ExecutorService的实现类ThreadPoolExecutor,可以看到ThreadPoolExecutor维护Worker的一个集合
Worker对象如:
并且查看ThreadPoolExecutor的execute方法
可以确定executorService对象持有内部线程池维护的Worker Set集合,而Worker内部持有一个thread和一个runnable,Worker本身也是一个runnable,如果曾经我们执行过execute的话,那他们之间的对应关系如下(当executorService没有出作用域的时候):
当未出函数执行作用域的时候executorService和thread均被gc root索引,当出函数作用域的时候,我们能够确定executorService索引已经释放,并且如开头所说thread还处于活着的状态(使用Android stuido查看是处于wait状态),分析如下:
查看 execute函数,当当前runnable执行的数量小于我们设置的核心线程数量的时候,如下if判断语句为true
if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); }
函数执行addWorker(只抓取核心部分代码)
private boolean addWorker(Runnable firstTask, boolean core) { try { w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { workers.add(w); } finally { mainLock.unlock(); } if (workerAdded) { ***t.start();*** workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted;}
接下去查看t.start(),从前面我们知道,此处的thread持有的runnable对象是worker,所以t.start执行的是worker的run方法,最后执行runworker
final void runWorker(Worker w) { try { while (task != null || (task = getTask()) != null) { try { try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
其中task是外部传入的runnable对象,我们看到此处有一个while循环,当当前的task执行完毕后,task为null,所以循环的执行我们到函数getTask查看
private Runnable getTask() { boolean timedOut = false; for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
其中我们看到workQueue.take();,我们知道队列的take方法在队列为空的情况下会阻塞线程。
至此,分析结论是线程池内部的核心线程被队列的take阻塞。文章只做简单分析,相关部分需要还得查看源码。针对该问题修改:针对移动端建议不使用Executors.newFixedThreadPool()创建ExecutorService,改用Executors.newCachedThreadPool(),同时申请创建的线程池,如果生命周期不跟进程的生命周期一样,建议手动调用shutdown关闭线程池中的线程
- Androdid中ExecutorService内存泄露原因分析
- Android中内存泄露的原因分析:
- 内存泄露原因分析
- java中内存泄露有几种?如何分析泄露原因
- Android中引起内存泄露的原因分析
- Android内存泄露分析之-内存泄露的原因
- JAVA内存泄露的原因分析
- android 内存泄露产生原因分析
- android中JNI调用时内存分布以及内存泄露原因分析
- android中内存泄露的原因
- android开发中内存泄露的原因
- Android OOM:内存管理分析和内存泄露原因总结
- 内存泄露的原因
- 内存泄露的原因
- 。net内存泄露原因
- 内存泄露的原因
- Android内存泄露原因
- java 内存泄露原因
- iOS获取设备常用健康数据
- C# 操作本地SQL Server实现增删改查
- Application.ProcessMessages
- 手机管理工具类 → AppPhoneMgr
- 设计模式--命令模式
- Androdid中ExecutorService内存泄露原因分析
- 【实战】嵌入式linux修改内核启动的LOGO界面
- tensorflow 学习笔记(2)-basic_example
- iOS 微信登录集成让人抓不着头脑的问题
- 每日一学(八) Android动画---Tween动画的xml实现
- Python bug整理
- 信息熵、信息增益与信息增益率
- 你真的了解html代码的事件,离开焦点和聚焦焦点的动作的意思吗?onblur、focus?它们是点击才可以触发,而不是鼠标悬停,鼠标悬停是hover
- 【0006】安装谷歌浏览器时,一直处于联网状态