WeakReference 弱引用與內存泄漏

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

版本更新后,作者為了避免內存泄漏,在源碼中添加了一個弱引用來存儲一個抽象類。

但在用戶有多個權限需要選擇的時候,抽象類有時候會被回收。當用戶確認權限之后,沒有調用到回調函數(shù)。

android內存空間分配

首先, 讓我們快速看下Android啟動流程. 與眾多基于Linux內核的系統(tǒng)類似, 啟動系統(tǒng)時, bootloader啟動內核和init進程. init進程分裂出更多名為"daemons(守護進程)"的底層的Linux進程, 諸如android debug deamon, USB deamon等. 這些守護進程處理底層硬件相關的接口.

隨后, init進程會啟動一個非常有意思的進程---"Zygote". 顧名思義, 這是一個Android平臺的非?;A的進程. 這個進程初始化了第一個VM, 并且預加載了framework和眾多App所需要的通用資源. 然后它開啟一個Socket接口來監(jiān)聽請求, 根據(jù)請求孵化出新的VM來管理新的App進程. 一旦收到新的請求, Zygote會基于自身預先加載的VM來孵化出一個新的VM創(chuàng)建一個新的進程.

啟動Zygote之后, init進程會啟動runtime進程. Zygote會孵化出一個超級管理進程---System Server. SystemServer會啟動所有系統(tǒng)核心服務, 例如Activity Manager Service, 硬件相關的Service等. 到此, 系統(tǒng)準備好啟動它的第一個App進程---Home進程了.

app launch

當啟動一個Android程序時,會啟動一個Dalvik VM進程,系統(tǒng)會給它分配固定的內存空間(16M,32M不定),這塊內存空間會映射到RAM上某個區(qū)域。然后這個Android程序就會運行在這塊空間上。Java里會將這塊空間分成Stack棧內存和Heap堆內存。stack里存放對象的引用,heap里存放實際對象數(shù)據(jù)。
在程序運行中會創(chuàng)建對象,如果未合理管理內存,比如不及時回收無效空間就會造成內存泄露,嚴重的話可能導致使用內存超過系統(tǒng)分配內存,即內存溢出OOM,導致程序卡頓甚至直接退出。

dalvik的Heap和Stack

也就是帶有回調函數(shù)的對象會放到內存堆中。當然,一般處理內存泄漏都是處理內存堆,這里只是提一下。

弱引用

在Java里, 當一個對象o被創(chuàng)建時, 它被放在Heap里. 當GC運行的時候, 如果發(fā)現(xiàn)沒有任何引用指向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<WeakReference<PermissionsResultAction>> 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]);
        }
    }

在源碼中執(zhí)行到這兒的時候,action有時候變成了null 。

在addPendingAction操作中有PermissionsResultAction(強引用)引用指向,但到notifyPermissionsChange()的時候PermissionsResultAction依然被系統(tǒng)回收了,回調函數(shù)不被執(zhí)行。

這是因為編譯器在發(fā)現(xiàn)進入while循環(huán)之后, PermissionsResultAction已經(jīng)沒有被使用, 所以進行了優(yōu)化(將其置空).

寫了一段測試代碼,對象最后的確被回收了。

  public static void main(String[] args) {

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


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


        int i = 0;
        WeakReference<PermissionAction> actionPermission = null;

        Iterator<WeakReference<PermissionAction>> 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寫法可能會導致內存泄漏。因為非靜態(tài)的內部類持有外部類的對象,而handler又會由于msg的處理而可能常駐在進程中,在activity或者service destroy后,不能及時被系統(tǒng)回收,導致內存泄漏。
建議寫法:

private static class OuterHandler extends Handler {
    private final WeakReference<MainActivity> mActivity;
         
    public OuterHandler(MainActivity activity) {
      mActivity = new WeakReference<MainActivity>(activity);
    }
         
    @Override
    public void handleMessage(Message msg) {
      MainActivity activity = mActivity.get();
      if (activity != null) {
        // do something...
      }
    }
  }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容