WeakReference 弱引用与内存泄漏

compile 'com.anthonycr.grant:permissions:1.1.2'

版本更新后,作者为了避免内存泄漏,在源码中添加了一个弱引用来存储一个抽象类。

但在用户有多个权限需要选择的时候,抽象类有时候会被回收。当用户确认权限之后,没有调用到回调函数。

android内存空间分配

首先, 让我们快速看下Android启动流程. 与众多基于Linux内核的系统类似, 启动系统时, bootloader启动内核和init进程. init进程分裂出更多名为"daemons(守护进程)"的底层的Linux进程, 诸如android debug deamon, USB deamon等. 这些守护进程处理底层硬件相关的接口.

随后, init进程会启动一个非常有意思的进程---"Zygote". 顾名思义, 这是一个Android平台的非常基础的进程. 这个进程初始化了第一个VM, 并且预加载了framework和众多App所需要的通用资源. 然后它开启一个Socket接口来监听请求, 根据请求孵化出新的VM来管理新的App进程. 一旦收到新的请求, Zygote会基于自身预先加载的VM来孵化出一个新的VM创建一个新的进程.

启动Zygote之后, init进程会启动runtime进程. Zygote会孵化出一个超级管理进程---System Server. SystemServer会启动所有系统核心服务, 例如Activity Manager Service, 硬件相关的Service等. 到此, 系统准备好启动它的第一个App进程---Home进程了.

WeakReference 弱引用与内存泄漏_第1张图片
app launch

当启动一个Android程序时,会启动一个Dalvik VM进程,系统会给它分配固定的内存空间(16M,32M不定),这块内存空间会映射到RAM上某个区域。然后这个Android程序就会运行在这块空间上。Java里会将这块空间分成Stack栈内存和Heap堆内存。stack里存放对象的引用,heap里存放实际对象数据。
在程序运行中会创建对象,如果未合理管理内存,比如不及时回收无效空间就会造成内存泄露,严重的话可能导致使用内存超过系统分配内存,即内存溢出OOM,导致程序卡顿甚至直接退出。

WeakReference 弱引用与内存泄漏_第2张图片
dalvik的Heap和Stack

也就是带有回调函数的对象会放到内存堆中。当然,一般处理内存泄漏都是处理内存堆,这里只是提一下。

弱引用

在Java里, 当一个对象o被创建时, 它被放在Heap里. 当GC运行的时候, 如果发现没有任何引用指向o, o就会被回收以腾出内存空间. 或者换句话说, 一个对象被回收, 必须满足两个条件: 1)没有任何引用指向它 2)GC被运行

   private synchronized void addPendingAction(@NonNull String[] permissions,
                                               @Nullable PermissionsResultAction action) {
        if (action == null) {
            return;
        }
        action.registerPermissions(permissions);
        mPendingActions.add(new WeakReference<>(action));
    }
    public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {
        int size = permissions.length;
        if (results.length < size) {
            size = results.length;
        }
        Iterator> iterator = mPendingActions.iterator();
        while (iterator.hasNext()) {
            PermissionsResultAction action = iterator.next().get();
            for (int n = 0; n < size; n++) {
                if (action == null || action.onResult(permissions[n], results[n])) {
                    iterator.remove();
                    break;
                }
            }
        }
        for (int n = 0; n < size; n++) {
            mPendingRequests.remove(permissions[n]);
        }
    }

在源码中执行到这儿的时候,action有时候变成了null 。

在addPendingAction操作中有PermissionsResultAction(强引用)引用指向,但到notifyPermissionsChange()的时候PermissionsResultAction依然被系统回收了,回调函数不被执行。

这是因为编译器在发现进入while循环之后, PermissionsResultAction已经没有被使用, 所以进行了优化(将其置空).

写了一段测试代码,对象最后的确被回收了。

  public static void main(String[] args) {

        List> mPendingActions = new ArrayList<>(1);


        mPendingActions.add(new WeakReference<>(new PermissionAction()));


        int i = 0;
        WeakReference actionPermission = null;

        Iterator> iterator = mPendingActions.iterator();

        if(iterator.hasNext()){
            actionPermission = iterator.next();
        }

        while (true) {
            PermissionAction action = actionPermission.get();

            if (action != null) {
                i++;
                System.out.println("Object is alive for " + i + " loops - " + action);
            } else {
                System.out.println("Object has been collected.");
                break;
            }

        }

    }
  • WeakReference的一个特点是它何时被回收是不可确定的, 因为这是由GC运行的不确定性所确定的. 所以, 一般用weak reference引用的对象是有价值被cache, 而且很容易被重新被构建, 且很消耗内存的对象.

虽然弱引用能让app避免了内存溢出的问题,但也带来了不确定性。

弱引用可以用于Handler,一般的Handler写法可能会导致内存泄漏。因为非静态的内部类持有外部类的对象,而handler又会由于msg的处理而可能常驻在进程中,在activity或者service destroy后,不能及时被系统回收,导致内存泄漏。
建议写法:

private static class OuterHandler extends Handler {
    private final WeakReference mActivity;
         
    public OuterHandler(MainActivity activity) {
      mActivity = new WeakReference(activity);
    }
         
    @Override
    public void handleMessage(Message msg) {
      MainActivity activity = mActivity.get();
      if (activity != null) {
        // do something...
      }
    }
  }

你可能感兴趣的:(WeakReference 弱引用与内存泄漏)