玩安卓必須要掌握的性能優化之內存泄漏

說起性能優化,大多數腦海裏便會出現啓動優化、UI繪製優化、內存優化等等。之前我也一直在做這些優化,一直沒有勇氣看內存泄漏的問題,覺得太難,但是,再難也要看啊,跑不了,那麼就來吧(做完發現其實也不難)。

內存泄漏的分析很簡單,Android Studio中就自帶了工具profile:

直接點擊這個按鈕就會運行應用並進入相應界面:

點擊圖片中的MEMORY就進入內存的頁面:

然後點擊左上角的下載按鈕(Record旁邊),進入詳細頁面,

可以選爲查看包內容,查看下當前運行的活動,然後右擊選中內容,選擇export,自定導出位置。

導出後需要使用SDK中的hprof-conv工具,這裏windows的用戶可以直接進入安卓的sdk文件夾下運行cmd即可,Mac用戶可以配置一個全局變量,由於我的系統版本是10.15,這裏我就寫zsh的配置方法了。

1.打開終端
2.打開 ~/.zshrc文件(如果無,則自動新建)
命令:

// 打開 ~/.zshrc文件(如果無,則自動新建)
open ~/.zshrc

3.設置環境變量

export PATH=${PATH}:/Users/zhujiang/Library/Android/sdk/platform-tools

4.保存並退出編輯
5.刷新環境變量生效

source ~/.zshrc

到這裏就都可以使用hprof-conv工具了,需要輸入以下命令將你導出的hprof文件轉換成mat-hprof文件,命令如下:

hprof-conv -z 導出的文件.hprof 轉換文件-mat.hprof

完成之後會出現的轉換的文件,接下來需要下載能分析MAT的工具,一般使用Memory Analyzer,下面是下載地址:

https://www.eclipse.org/mat/downloads.php

下載完畢後雙擊文件如果報錯的話,這是eclipse的一個bug。

解決辦法: 右鍵mat顯示包內容,進入Contents->MacOS下面,會有一個MemoryAnalyzer的命令。

打開終端,進入此路徑找到MemoryAnalyzer,運行以下命令:

./MemoryAnalyzer -data dump文件所在文件夾路徑

這樣即可啓動成功:

打開之後選擇如下選項打開剛纔轉換完成的文件:

打開之後選擇我下面的選項,然後點擊finish,如果出錯別管它,繼續進行:

進入之後選擇Overview,然後點擊Histogram。

之後就可以進入類的頁面,可以在裏面進行篩選:

下面是篩選的方法:

找見需要優化的類,按照下面的方法打開:

打開之後是如下的頁面:

發現有一個mContext持有活動的引用,導致活動無法銷燬。找到原因之後進行解決,咱們需要短了它的引用鏈,使GC可以清理調它。代碼不多,在活動的onDestory()中進行銷燬就行。

public static void fixInputMethodManagerLeak(Context destContext) {
        if (destContext == null) {
            return;
        }

        InputMethodManager imm = (InputMethodManager) destContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm == null) {
            return;
        }

        String [] arr = new String[]{"mCurRootView", "mServedView", "mNextServedView"};
        Field f;
        Object obj_get;
        for (String param : arr) {
            try {
                f = imm.getClass().getDeclaredField(param);
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                obj_get = f.get(imm);
                if (obj_get instanceof View) {
                    View v_get = (View) obj_get;
                    if (v_get.getContext() == destContext) { // 被InputMethodManager持有引用的context是想要目標銷燬的
                        f.set(imm, null); // 置空,破壞掉path to gc節點
                    } else {
                        // 不是想要目標銷燬的,即爲又進了另一層界面了,不要處理,避免影響原邏輯,也就不用繼續for循環了
                        break;
                    }
                }
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }

好了,這就是一個簡單的內存泄漏問題。如果你的頁面中有其他頁面沒有銷燬,那就證明你的頁面存在內存泄漏,就需要來這樣進行查看。使用Mat工具方法有很多,大家可以多搜索一下。先到這裏吧。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章