性能優(yōu)化—防止內存泄漏和溢出

如何找到項目中存在的內存泄露的這些地方呢荠商?

1.確定是否存在內存泄露
  • Android Monitors的內存分析

最直觀的看內存增長情況,知道該動作是否發(fā)生內存泄露蜕企。動作發(fā)生之前: GC完后內存1.4M; 動作發(fā)生之后:GC完后內存1.6M

  • 使用MAT內存分析工具

MAT分析heap的總內存占用大小來初步判斷是否存在泄露Heap視圖中有一個Type叫做data object劲厌,即數據對象,也就是我們的程序中大量存在的類類型的對象刽射。在data object一行中有一列是“Total Size”军拟,其值就是當前進程中所有Java數據對象的內存總量,一般情況下誓禁,這個值的大小決定了是否會有內存泄漏懈息。我們反復執(zhí)行某一個操作并同時執(zhí)行GC排除可以回收掉的內存,注意觀察data object的Total Size值摹恰,正常情況下Total Size值都會穩(wěn)定在一個有限的范圍內辫继,也就是說由于程序中的的代碼良好怒见,沒有造成對象不被垃圾回收的情況。
反之如果代碼中存在沒有釋放對象引用的情況姑宽,隨著操作次數的增多Total Size的值會越來越大遣耍。那么這里就已經初步判斷這個操作導致了內存泄露的情況。

2.先找懷疑對象(哪些對象屬于泄露的)

MAT對比操作前后的hprof來定位內存泄露是泄露了什么數據對象低千。(這樣做可以排除一些對象配阵,不用后面去查看所有被引用的對象是否是嫌疑)快速定位到操作前后所持有的對象哪些是增加了(GC后還是比之前多出來的對象就可能是泄露對象嫌疑犯)。
技巧:Histogram中還可以對對象進行Group示血,比如選擇Group By Package更方便查看自己Package中的對象信息棋傍。

3. MAT分析hprof來定位內存泄露的原因所在。

(哪個對象持有了上面懷疑出來的發(fā)生泄露的對象)

  • 1 Dump出內存泄露“當時”的內存鏡像hprof难审,分析懷疑泄露的類瘫拣;
  • 2 把上面2得出的這些嫌疑犯一個一個排查個遍。步驟:
    2.1 進入Histogram告喊,過濾出某一個嫌疑對象類
    2.2 然后分析持有此類對象引用的外部對象(在該類上面點擊右鍵List Objects--->with incoming references)
    2.3 再過濾掉一些弱引用麸拄、軟引用、虛引用黔姜,因為它們遲早可以被GC干掉不屬于內存泄露 (在類上面點擊右鍵Merge Shortest Paths to GC Roots--->exclude all phantom/weak/soft etc.references)
    2.4 逐個分析每個對象的GC路徑是否正常此時就要進入代碼分析此時這個對象的引用持有是否合理拢切,這就要考經驗和體力了!(比如上課的例子中:旋轉屏幕后MainActivity有兩個秆吵,肯定MainActivity發(fā)生泄露了.

那誰導致他泄露的呢淮椰?

原來是我們的CommonUtils類持有了旋轉之前的那個MainActivity他,那是否合理纳寂?結合邏輯判斷當然不合理主穗,由此找到內存泄露根源是CommonUtils類持有了該MainActivity實例造成的。

怎么解決毙芜?

罪魁禍首找到了忽媒,怎么解決應該不難了,不同情況解決辦法不一樣腋粥,要靠你的智慧了晦雨。)context.getapplictioncontext()可以嗎?
可以0濉金赦!只要讓CommonUtils類不直接只有MainActivity的實例就可以了。


4 判斷一個應用里面內存泄露避免得很好对嚼,怎么看?

當app退出的時候绳慎,這個進程里面所有的對象應該就都被回收了纵竖,尤其是很容易被泄露的(View漠烧,Activity)是否還內存當中。
可以讓app退出以后靡砌,查看系統(tǒng)該進程里面的所有的View已脓、Activity對象是否為0.
工具:使用AndroidStudio--AndroidMonitor--System Information--Memory Usage查看Objects里面的views和Activity的數量是否為0.
命令行模式:

5 內存泄露經常出現(xiàn)的例子
  • 內存泄露(Memory Leak)
    進程中某些對象已經沒有使用價值了,但是他們卻還可以直接或者間接地被引用到GC Root導致無法回收通殃。當內存泄露過多的時候度液,再加上應用本身占用的內存,日積月累最終就會導致內存溢出OOM.
  • 內存溢出(OOM)
    當應用占用的heap資源超過了Dalvik虛擬機分配的內存就會內存溢出画舌。比如:加載大圖片堕担。
    • 5.1 靜態(tài)變量引起的內存泄露
      當調用getInstance時,如果傳入的context是Activity的context曲聂。只要這個單
      利沒有被釋放霹购,那么這個Activity也不會被釋放一直到進程退出才會釋放。

      public class CommUtil {
      private static CommUtil instance;
      private Context context;
      private CommUtil(Context context){
      this.context = context;
      }
        public static CommUtil getInstance(Context mcontext){
      if(instance == null){
            instance = new CommUtil(mcontext);
      }
          //        else{
          //            instance.setContext(mcontext);
          //        }
      return instance;
            }
      
    • 5.2.非靜態(tài)內部類引起內存泄露
      (包括匿名內部類)錯誤的示范:

      public void loadData(){//隱式持有MainActivity實例朋腋。MainActivity.this.a
      new Thread(new Runnable() {
          @Override
          public void run() {
          while(true){
              try {
              //int b=a;
              Thread.sleep(1000);
              } catch (InterruptedException e) {
              e.printStackTrace();
              }
          }
          }
      }).start();
      }
      

      解決方案

      將非靜態(tài)內部類修改為靜態(tài)內部類齐疙。(靜態(tài)內部類不會隱士持有外部類)

當使用軟引用或者弱引用的時候,MainActivity難道很容易或者可以被GC回收嗎旭咽?GC回收的機制是什么贞奋?
當MainActivity不被任何的對象引用,才會被回收穷绵。雖然Handler里面用的是軟引用/弱引用轿塔,但是并不意味著不存在其他的對象引用該MainActivity。我連MainActivity都被回收了请垛,那他里面的Handler還玩?zhèn)€屁催训。

    • 5.3.不需要用的監(jiān)聽未移除會發(fā)生內存泄露
      // tv.setOnClickListener();//監(jiān)聽執(zhí)行完回收對象
      //add監(jiān)聽,放到集合里面
      tv.getViewTreeObserver().addOnWindowFocusChangeListener
      (new ViewTreeObserver.OnWindowFocusChangeListener() {
      @Override
      public void onWindowFocusChanged(boolean b) {
      //監(jiān)聽view的加載宗收,view加載出來的時候漫拭,計算他的寬高等。

             //計算完后混稽,一定要移除這個監(jiān)聽
             tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
         }
           });
      
       例子2:
       SensorManager sensorManager = getSystemService(SENSOR_SERVICE);
       Sensor sensor =   sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
       sensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_FASTEST);
       //不需要用的時候記得移除監(jiān)聽
       sensorManager.unregisterListener(listener);
      
    • 5.4.資源未關閉引起的內存泄露情況

    比如:BroadCastReceiver采驻、Cursor、Bitmap匈勋、IO流礼旅、自定義屬性attribute attr.recycle()回收。
    當不需要使用的時候洽洁,要記得及時釋放資源痘系。否則就會內存泄露。

    • 5.5.無限循環(huán)動畫

沒有在onDestroy中停止動畫饿自,否則Activity就會變成泄露對象汰翠。
比如:輪播圖效果龄坪。

6 常用代碼片段

6.1 Handler防止內存泄漏
如果Handler在Activity退出的時候,有可能還活著复唤,這時候就會一直持有Activity健田。

private static class MyHandler extends Handler{
    // private MainActivity mainActivity;//直接持有了一個外部類的強引用,會內存泄露
    //設置軟引用保存佛纫,當內存一發(fā)生GC的時候就會回收妓局。
    private WeakReference<MainActivity> mainActivity;

    public MyHandler(MainActivity mainActivity) {
        this.mainActivity = new WeakReference<MainActivity>(mainActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity main =  mainActivity.get();
        if(main==null||main.isFinishing()){
            return;
        }
        switch (msg.what){
            case 0:
                //加載數據
        //MainActivity.this.a;//有時候確實會有這樣的需求:需要引用外部類的資源。怎么辦呈宇?
                int b = main.a;
                break;
        }
    }
};

6.2 弱引用的使用
當垃圾回收時就會釋放那些沒有被引用的內存好爬。
如下面,當key值View沒有被引用時攒盈,那么map集合就會丟棄次值抵拘。

public class ListenerCollector {
    /**
     * WeakHashMap,此種Map的特點是型豁,當除了自身有對key的引用外僵蛛,此key沒有其他引用那么此map
     * 會自動丟棄此值
     * 例如:int a = 1;
     * Map weakmap = new WeakHashMap();
     * weakmap.put(a, "aaa");z  x
     * a = null
     * //此時weakmap里面的a會被丟棄。
     */
    static private WeakHashMap<View, MyView.MyListener> sListener = new WeakHashMap<>();

    public void setsListener(View view, MyView.MyListener listener) {
        sListener.put(view, listener);
    }

    public static void clearListeners() {
        //移除所有監(jiān)聽迎变。
        sListener.clear();
    }
}

6.3 使匿名內部類變成靜態(tài)匿名內部類
靜態(tài)匿名內部類不屬于外部類充尉,不持有外部類的引用。下面的線程有可能在activity已經被finish了衣形,還沒有執(zhí)行結束驼侠,因此需要將其改成static。

//加上static谆吴,里面的匿名內部類就變成了靜態(tài)匿名內部類
//隱士持有MainActivity實例倒源。MainActivity.this.a
public static void loadData(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            while(true){
                try {
                    int b=a;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市句狼,隨后出現(xiàn)的幾起案子笋熬,更是在濱河造成了極大的恐慌,老刑警劉巖腻菇,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胳螟,死亡現(xiàn)場離奇詭異,居然都是意外死亡筹吐,警方通過查閱死者的電腦和手機糖耸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丘薛,“玉大人嘉竟,你說我怎么就攤上這事。” “怎么了周拐?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵铡俐,是天一觀的道長。 經常有香客問我妥粟,道長,這世上最難降的妖魔是什么吏够? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任勾给,我火速辦了婚禮,結果婚禮上锅知,老公的妹妹穿的比我還像新娘播急。我一直安慰自己,他們只是感情好售睹,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布桩警。 她就那樣靜靜地躺著,像睡著了一般昌妹。 火紅的嫁衣襯著肌膚如雪捶枢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天飞崖,我揣著相機與錄音烂叔,去河邊找鬼。 笑死固歪,一個胖子當著我的面吹牛蒜鸡,可吹牛的內容都是我干的。 我是一名探鬼主播牢裳,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼逢防,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蒲讯?” 一聲冷哼從身側響起忘朝,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伶椿,沒想到半個月后辜伟,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡脊另,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年导狡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偎痛。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡旱捧,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情枚赡,我是刑警寧澤氓癌,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站贫橙,受9級特大地震影響贪婉,放射性物質發(fā)生泄漏。R本人自食惡果不足惜卢肃,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一疲迂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莫湘,春花似錦尤蒿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至忙芒,卻和暖如春示弓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匕争。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工避乏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人甘桑。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓拍皮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親跑杭。 傳聞我的和親對象是個殘疾皇子铆帽,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內容