Android效能最佳化之記憶體流失

來源:互聯網
上載者:User

標籤:選擇   查看   register   string   off   sdk   ext   視窗   無限   

綜述

  記憶體流失(memory leak)是指由於疏忽或錯誤造成程式未能釋放已經不再使用的記憶體。那麼在Android中,當一個對象持有Activity的引用,如果該對象不能被系統回收,那麼當這個Activity不再使用時,這個Activity也不會被系統回收,那這麼以來便出現了記憶體流失的情況。在應用中內出現一次兩次的記憶體流失擷取不會出現什麼影響,但是在應用長時間使用以後,若是存在大量的Activity無法被GC回收的話,最終會導致OOM的出現。那麼我們在這就來分析一下導致記憶體流失的常見因素並且如何去檢測記憶體流失。

導致記憶體流失的常見因素 情景一:靜態Activity和View

  靜態變數Activity和View會導致記憶體流失,在下面這段代碼中對Activity的Context和TextView設定為靜態對象,從而產生記憶體流失。

import android.content.Context;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.TextView;public class MainActivity extends AppCompatActivity {    private static Context context;    private static TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        context = this;        textView = new TextView(this);    }}
情景二:Thread,匿名類,內部類

  在下面這段代碼中存在一個非靜態匿名類對象Thread,會隱式持有一個外部類的引用LeakActivity,從而導致記憶體流失。同理,若是這個Thread作為LeakActivity的內部類而不是匿名內部類,他同樣會持有外部類的引用而導致記憶體流失。在這裡只需要將為Thread匿名類定義成靜態內部類即可(靜態內部類不會持有外部類的一個隱式引用)。

public class LeakActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_leak);        leakFun();    }    private void leakFun(){        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(Integer.MAX_VALUE);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        });    }}
情景三:動畫

  在屬性動畫中有一類無限迴圈動畫,如果在Activity中播放這類動畫並且在onDestroy中去停止動畫,那麼這個動畫將會一直播放下去,這時候Activity會被View所持有,從而導致Activity無法被釋放。解決此類問題則是需要早Activity中onDestroy去去調用objectAnimator.cancel()來停止動畫。

public class LeakActivity extends AppCompatActivity {    private TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_leak);        textView = (TextView)findViewById(R.id.text_view);        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,"rotation",0,360);        objectAnimator.setRepeatCount(ValueAnimator.INFINITE);        objectAnimator.start();    }}
情景四:Handler

  對於Handler的記憶體流失在(Android的訊息機制——Handler的工作過程)[http://blog.csdn.net/ljd2038/article/details/50889754]這篇文章中已經詳細介紹,就不在贅述。

情景五:第三方庫使用不當

  對於EventBus,RxJava等一些第三開源架構的使用,若是在Activity銷毀之前沒有進行解除訂閱將會導致記憶體流失。

使用MAT檢測記憶體流失

  對於常見的記憶體泄露進行介紹完以後,在這裡再看一下使用MAT(Memory Analysis Tool)來檢測記憶體泄露。MAT的為:http://www.eclipse.org/mat/downloads.php。
  下面來看一段會導致記憶體泄露的錯誤碼。

public class LeakActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_leak);        EventBus.getDefault().register(this);    }    @Subscribe    public void subscriber(String s){    }}

  在上面這段代碼中有會導致記憶體流失,原因是EventBus沒有解除註冊。下面就以這段代碼為例來看一下如何分析記憶體流失。
  開啟AndroidStudio中的Monitors可以看到如下介面。

  
  在這裡可以看到在應用剛啟動的時候,所佔用的記憶體為15M,然後我們現在開始操作APP,反覆進入退出LeakActicity。點擊上如中的GC按鈕。這時候我們在看一下記憶體使用量情況。

  在這裡我們可以看到,記憶體一直在持續增加,已經達到33M,並且無法被GC所回收。所以我們可以判斷,這時候必然出現記憶體流失的情形。那麼現在再點擊Dump Java Heap按鈕,在captures視窗看到產生得hprof檔案。但這時候所產生的hprof檔案不是標準格式的,我們需要通過SDK所提供的工具hprof-conv進行轉化,該工具在SDK的platform-tools目錄下。執行命令如下:

  hprof-conv XXX.hprof converted-dump.hprof

  當然在AndroidStudio中可以省去這一步,可以直接匯出標準格式的hprof檔案。

  
  這時候可以通過MAT工具來開啟匯出的hprof檔案。開啟介面如所示:

  在MAT中我們最常用的就是Histogram和Dominator Tree,他們分別對應中的A和B按鈕。Histogram可以看出記憶體中不同類型的buffer的數量和佔用記憶體的大小,而Dominator Tree則是把記憶體中的對象按照從大到小的順序進行排序,並且可以分析對象之間的參考關聯性。在這裡再來介紹一下MAT中兩個符號的含義。

  • ShallowHeap:對象自身佔用的記憶體大小,不包括他引用的對象
  • RetainedHeap:對象自身佔用的記憶體大小並且加上它直接或者間接引用對象的大小
Histogram

   由於在Android中一般記憶體流失大多出現在Acivity中,這時候可以點擊Histogram按鈕,並搜尋
Activity。

   在這裡可以看出LeakActivity存在69個對象,基本上可以斷定存在記憶體流失的情形,這時候便可以通過查看GC對象的引用鏈來進行分析。點擊滑鼠右鍵選擇Merge Shortest paths to GC Roots並選擇exclude weak/soft references來排除弱引用和軟引用。

  
   在排除軟引用和弱引用以後如所示:

   在這裡可以看出由於EventBus導致的LeakActivity記憶體流失。
   在Histogram中還可以查看一個對象包含了那些對象的引用。例如,現在要查看LeakActivity所包含的引用,可以點擊滑鼠右鍵,選擇list objects中的with incoming reference。而with outcoming reference表示選中對象持有那些對象的引用。

Dominator Tree

   現在我們點擊這時候可以點擊Dominator Tree按鈕,並搜尋
Activity。可以看到如所示:

  
   在這裡可以看到存在大量的LeakActivity。然後點擊滑鼠右鍵選擇Path To GC Roots->exclude weak/soft references來排除弱引用和軟引用。

  
   之後可以看到如下結果,依然是EventBus導致的記憶體流失:

總結

  記憶體流失往往被我們所忽略,但是當大量的記憶體流失以後導致OOM。它所造成的影響也是不容小覷的。當然除了上述記憶體流失的分析以為我們還可以通過LeakCanary來分析記憶體流失。對於LeakCanary的使用在這裡就不在進行詳細介紹。

Android效能最佳化之記憶體流失

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.