Androdid中ExecutorService内存泄露原因分析

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关闭线程池中的线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值