乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      【原創(chuàng)】Android 系統(tǒng)穩(wěn)定性

       老匹夫 2014-02-22

      文章都為原創(chuàng),轉(zhuǎn)載請注明出處,未經(jīng)允許而盜用者追究法律責(zé)任。 

      很久之前寫的了,留著有點(diǎn)浪費(fèi),共享之。 

      編寫者:李文棟   微博關(guān)注: 云且留豬

      2.3 如何分析內(nèi)存溢出問題

      無論怎么小心,想完全避免 bad code 是不可能的,此時就需要一些工具來幫助我們檢查代碼中是否存在會造成內(nèi)存泄漏的地方。

      既然要排查的是內(nèi)存問題,自然需要與內(nèi)存相關(guān)的工具,DDMSMAT就是兩個非常好的工具。下面詳細(xì)介紹。

      2.3.1 內(nèi)存監(jiān)測工具 DDMS --> Heap

      Android tools 中的 DDMS 就帶有一個很不錯的內(nèi)存監(jiān)測工具 Heap(這里我使用 eclipse ADT 插件,并以真機(jī)為例,在模擬器中的情況類)。用 Heap 監(jiān)測應(yīng)用進(jìn)程使用內(nèi)存情況的步驟如下:

      1. 啟動 eclipse 后,切換到 DDMS 透視圖,并確認(rèn) Devices 視圖、Heap 視圖都是打開的;

      2. 將手機(jī)通過 USB 鏈接至電腦,鏈接時需要確認(rèn)手機(jī)是處于“USB 調(diào)試”模式,而不是作為“Mass Storage”;

      3. 鏈接成功后,在 DDMS Devices 視圖中將會顯示手機(jī)設(shè)備的序列號,以及設(shè)備中正在運(yùn)行的部分進(jìn)程信息;

      4. 點(diǎn)擊選中想要監(jiān)測的進(jìn)程,比如 system_process 進(jìn)程;

      5. 點(diǎn)擊選中 Devices 視圖界面中最上方一排圖標(biāo)中的“Update Heap”圖標(biāo);

      6. 點(diǎn)擊 Heap 視圖中的“Cause GC”按鈕;

      7. 此時在 Heap 視圖中就會看到當(dāng)前選中的進(jìn)程的內(nèi)存使用量的詳細(xì)情況[如圖所示]


       

      說明:

      • 點(diǎn)擊“Cause GC”按鈕相當(dāng)于向虛擬機(jī)請求了一次 gc 操作;

      • 當(dāng)內(nèi)存使用信息第一次顯示以后,無須再不斷的點(diǎn)擊“Cause GC”Heap 視圖界面會定時刷新,在對應(yīng)用的不斷的操作過程中就可以看到內(nèi)存使用的變化;

      • 內(nèi)存使用信息的各項(xiàng)參數(shù)根據(jù)名稱即可知道其意思,在此不再贅述。如何才能知道我們的程序是否有內(nèi)存泄漏的可能性呢。這里需要注意一個值:Heap 圖中部有一個 Type 叫做 data object,即數(shù)據(jù)對象,也就是我們的程序中大量存在的類類型的對象。在 data object 一行中有一列是“Total Size”,其值就是當(dāng)前進(jìn)程中所有 Java 數(shù)據(jù)對象的內(nèi)存總量,一般情況下,這個值的大小決定了是否會有內(nèi)存泄漏??梢赃@樣判斷:

      • 不斷的操作當(dāng)前應(yīng)用,同時注意觀察 data object Total Size ;

      • 正常情況下 Total Size 值都會穩(wěn)定在一個有限的范圍內(nèi),也就是說由于程序中的代碼良,沒有造成對象不被垃圾回收的情況,所以說雖然我們不斷的操作會不斷的生成很多對象 而在虛擬機(jī)不斷的進(jìn)行 GC 的過程中,這些對象都被回收了,內(nèi)存占用量會會落到一個穩(wěn)定的水平;

      • 反之如果代碼中存在沒有釋放對象引用的情況,則 data object Total Size 值在每次 GC后不會有明顯的回落,隨著操作次數(shù)的增多 Total Size 的值會越來越大,直到到達(dá)一個上限后導(dǎo)致進(jìn)程被 kill 掉。

      • 此處已 system_process 進(jìn)程為例,在我的測試環(huán)境中 system_process 進(jìn)程所占用的內(nèi)存的data object Total Size 正常情況下會穩(wěn)定在 2.2~2.8 之間,而當(dāng)其值超過 3.55 后進(jìn)程就會被kill。

      總之,使用 DDMS Heap 視圖工具可以很方便的確認(rèn)我們的程序是否存在內(nèi)存泄漏的可能性。

      2.3.2 內(nèi)存分析工具 MAT(Memory Analyzer Tool)

      如果使用 DDMS 確實(shí)發(fā)現(xiàn)了我們的程序中存在內(nèi)存泄漏,那又如何定位到具體出現(xiàn)問題的代碼片段,最終找到問題所在呢?如果從頭到尾的分析代碼邏輯,那肯定會把人逼瘋,特別是在維護(hù)別人寫的代碼的時候。這里介紹一個極好的內(nèi)存分析工具 -- Memory Analyzer Tool(MAT)。

      MAT 是一個 Eclipse 插件,同時也有單獨(dú)的 RCP 客戶端。官方下載地址、MAT 介紹和詳細(xì)的使用教程請參見:www.eclipse.org/mat,在此不進(jìn)行說明了。另外在 MAT 安裝后的幫助文檔里也有完備的使用教程。在此僅舉例說明其使用方法。我自己使用的是 MAT eclipse 插件,使用插件要比 RCP 稍微方便一些。

      MAT通過解析Hprof文件來分析內(nèi)存使用情況。HPROF其實(shí)是在J2SE5.0中包含的用來分析CPU使用和堆內(nèi)存占用的日志文件,實(shí)質(zhì)上是虛擬機(jī)在某一時刻的內(nèi)存快照,dalvik中也包含了這樣的工具,但是其文件格式和JVM的格式不完全相同,可以用SDK中自帶的hprof-conv工具進(jìn)行轉(zhuǎn)換,例如:

      $./hprof-conv raw.hprof converted.hprof

      可以使用hprof文件配合traceview來分析CPU使用情況(函數(shù)調(diào)用時間),此處僅僅討論用它來分析內(nèi)存使用情況,關(guān)于hprof的其他信息可以查看:http://java./developer/technicalArticles/Programming/HPROF.html

      以及Android源碼中的/dalvik/docs/heap-profiling.html文件(這個比較重要,建議看看,例如kill -10Android2.3中已經(jīng)不支持了)。

      使用 MAT 進(jìn)行內(nèi)存分析需要幾個步驟,包括:生成.hprof 文件、打開 MAT 并導(dǎo)入hprof文件、使用 MAT 的視圖工具分析內(nèi)存。以下詳細(xì)介紹。

      1. 生成hprof 文件

      生成hprof 文件的方法有很多,而且 Android 的不同版本中生成hprof 的方式也稍有差,我使用的版本的是 2.1,各個版本中生成hprof 文件的方法請參考:

      http://android.git./?p=platform/dalvik.git;a=blob_plain;f=docs/heap-profiling.html;hb=HEAD。

      (1) 打開 eclipse 并切換到 DDMS 透視圖,同時確認(rèn) Devices、Heap logcat 視圖已經(jīng)打開了 ;

      (2) 將手機(jī)設(shè)備鏈接到電腦,并確保使用“USB 調(diào)試”模式鏈接,而不是“Mass Storage“;

      (3) 鏈接成功后在 Devices 視圖中就會看到設(shè)備的序列號,和設(shè)備中正在運(yùn)行的部分進(jìn)程;

      (4) 點(diǎn)擊選中想要分析的應(yīng)用的進(jìn)程,在 Devices 視圖上方的一行圖標(biāo)按鈕中,同時選中“Update Heap”和“Dump HPROF file”兩個按鈕;

      (5) 這是 DDMS 工具將會自動生成當(dāng)前選中進(jìn)程的.hprof 文件,并將其進(jìn)行轉(zhuǎn)換后存放在sdcard 當(dāng)中,如果你已經(jīng)安裝了 MAT 插件,那么此時 MAT 將會自動被啟用,并開始對.hprof文件進(jìn)行分析;

      注意: (4)步和第(5)步能夠正常使用前提是我們需要有 sdcard,并且當(dāng)前進(jìn)程有向 sdcard中寫入的權(quán)限(WRITE_EXTERNAL_STORAGE),否則.hprof 文件不會被生成,在 logcat 中會顯示諸如ERROR/dalvikvm(8574): hprof: can't open /sdcard/com.xxx.hprof-hptemp: Permission denied.的信息。

      如果我們沒有 sdcard,或者當(dāng)前進(jìn)程沒有向 sdcard 寫入的權(quán)限(system_process) 我們可以這樣做:

      (6) 在當(dāng)前程序中,例如 framework 中某些代碼中,可以使用 android.os.Debug 中的:

      public static void dumpHprofData(String fileName) throws IOException

      方法,手動的指定.hprof 文件的生成位置。例如:

      xxxButton.setOnClickListener(new View.OnClickListener() {

              public void onClick(View view) {

                      android.os.Debug.dumpHprofData("/data/temp/myapp.hprof");

                      ... ...

              }

      }

      上述代碼意圖是希望在 xxxButton 被點(diǎn)擊的時候開始抓取內(nèi)存使用信息,并保存在我們指定的位置:/data/temp/myapp.hprof,這樣就沒有權(quán)限的限制了,而且也無須用 sdcard。但要保證/data/temp 目錄是存在的。這個路徑可以自己定義,當(dāng)然也可以寫成 sdcard 當(dāng)中的某個路徑。

      如果不確定進(jìn)程什么時候會OOM,例如我們在跑Monkey的過程中出現(xiàn)了OOM,此時最好的辦法就是讓程序在出現(xiàn)OOM之后,而沒有將OOM的錯誤信息拋給虛擬機(jī)之前就將進(jìn)程的hprof抓取出來。方法也很簡單,只需要在代碼中你認(rèn)為會拋出OutOfMemoryError的地方try...catch,并在catch塊中使用android.os.Debug.dumpHprofData(String file)方法就可以請求虛擬機(jī)dumphprof到你指定的文件中。例如我們之前為了排查應(yīng)用進(jìn)程主線程中發(fā)生的OOM,就在ActivityThread.main()方法中添加了以下代碼:

      try {

              Looper.loop();

      } catch (OutOfMemoryError e) {

              String file = "path_to_file.hprof"

              ... ...

              try {

                      android.os.Debug.dumpHprofData(file);

              } catch (IOException e1) {

                      e1.printStackTrace();

              }

      }

      在設(shè)置hprof的文件路徑時,需要考慮權(quán)限問題,包括SD卡訪問權(quán)限、/data分區(qū)私有目錄訪問權(quán)限。

      之所以在以上位置添加代碼,是因?yàn)樵趹?yīng)用進(jìn)程主線程中如果發(fā)生異常和錯誤沒有捕獲,最終都會從Looper.loop()中拋出來。如果你需要排查在其他線程,或者framework中的OOM問題時,同樣可以在適當(dāng)?shù)奈恢檬褂?/span>android.os.Debug.dumpHprofData(String file)方法dump hprof文件。

      有了hprof文件,并且用hprof-conv轉(zhuǎn)換格式之后,第二步就可以用MemoryAnalyzerToolMAT)工具來分析內(nèi)存使用情況了。

      2. 使用 MAT 導(dǎo)入hprof 文件

      (1) 如果是 eclipse 自動生成的hprof 文件,可以使用 MAT 插件直接打開(可能是比較新的 ADT才支持);

      (2) 如 果 eclipse 自 動 生 成 的 .hprof 文 件 不 能 被 MAT 直 接 打 開 , 或 者 是 使 用android.os.Debug.dumpHprofData()方法手動生成的hprof 文件,則需要將hprof 文件進(jìn)行轉(zhuǎn),轉(zhuǎn)換的方法:

      例如我將hprof 文件拷貝到 PC 上的/ANDROID_SDK/tools 目錄下,并輸入命令 hprof-conv xxx.hprof yyy.hprof,其中 xxx.hprof 為原始文件,yyy.hprof 為轉(zhuǎn)換過后的文件。轉(zhuǎn)換過后的文件自動放在/ANDROID_SDK/tools 目錄下。OK,到此為止,hprof 文件處理完畢,可以用來分析內(nèi)存泄露情況了。

      (3) Eclipse 中點(diǎn)擊 Windows->Open Perspective->Other->Memory Analyzer,或者打 Memory Analyzer Tool RCP。在 MAT 中點(diǎn)擊 File->Open File,瀏覽并導(dǎo)入剛剛轉(zhuǎn)換而得到的hprof文件。

      3. 使用 MAT 的視圖工具分析內(nèi)存

      導(dǎo)入hprof 文件以后,MAT 會自動解析并生成報告,點(diǎn)擊 Dominator Tree,并按 Package分組,選擇自己所定義的 Package 類點(diǎn)右鍵,在彈出菜單中選擇 List objects->With incoming references。這時會列出所有可疑類,右鍵點(diǎn)擊某一項(xiàng),并選擇 Path to GC Roots -> exclude weak/soft references,會進(jìn)一步篩選出跟程序相關(guān)的所有有內(nèi)存泄露的類。據(jù)此,可以追蹤到代碼中的某一個產(chǎn)生泄露的類。

      MAT 的界面如下圖所示。



       

      了解 MAT 中各個視圖的作用很重要,例如 www.eclipse.org/mat/about/screenshots.php 中介紹的。

      總之使用 MAT 分析內(nèi)存查找內(nèi)存泄漏的根本思路,就是找到哪個類的對象的引用沒有被釋放,找到?jīng)]有被釋放的原因,也就可以很容易定位代碼中的哪些片段的邏輯有問題了。下一節(jié)將用一個示例來說明MAT詳細(xì)的使用過程。

      2.3.3 MAT使用方法

      1. 構(gòu)建演示程序

      首先需要構(gòu)建一個演示程序,并獲取hprof文件。程序很簡單,按下Button后就循環(huán)地new自定義對象SomeObj,并將對象addArrayList中,直到拋出OutOfMemoryError,此時會捕獲該錯誤,同時使用android.os.Debug.dumpHprofData方法dump該進(jìn)程的內(nèi)存快照到/sdcard/oom.hprof文件中。

      package com.demo.oom;

       

      import java.io.IOException;

      import java.util.ArrayList;

       

      import android.app.Activity;

      import android.os.Bundle;

      import android.widget.Button;

      import android.view.View;

       

      publicclass OOMDemoActivity extends Activity implements View.OnClickListener {

      privatestaticfinal String HPROF_FILE = "/sdcard/oom.hprof";

      private Button mBtn;

      private ArrayList<SomeObj> list = new ArrayList<SomeObj>();

       

      @Override

      publicvoid onCreate(Bundle savedInstanceState) {

              super.onCreate(savedInstanceState);

              setContentView(R.layout.main);

       

              mBtn = (Button)findViewById(R.id.btn);

              mBtn.setOnClickListener(this);

      }

       

      @Override

      publicvoid onClick(View v) {

              try {

                      while (true) {

                              list.add(new SomeObj());

                      }

              } catch (OutOfMemoryError e) {

                      try {

                              android.os.Debug.dumpHprofData(HPROF_FILE);

                              throw e;

                      } catch (IOException e1) {

                              e1.printStackTrace();

                      }

              }

      }

       

              private class SomeObj {

                      private static final intDATA_SIZE = 1 * 1024 * 1024;

                      private byte[] data;

                      SomeObj() {

                              data = newbyte[DATA_SIZE];

                      }

              }

      }

      因?yàn)橐獙懭?/span>SDCard,所以要在AndroidManifest.xml中聲明WRITE_EXTERNAL_STORAGE的權(quán)限。

      注意:演示程序中是使用平臺API來獲取dump hprof文件的,你也可以使用ADTDDMS工具來dump。每個hprof都是針對某一個Java進(jìn)程的,如果你dump的是com.demo.oom進(jìn)程的hprof,是無法用來分析system_server進(jìn)程的內(nèi)存情況的。

      編譯并運(yùn)行程序最終會在SDCard中生成oom.hprof文件,log中會打印相關(guān)的日志信息,請留意紅色字體:

      I/dalvikvm(1238): hprof: dumping heap strings to "/sdcard/oom.hprof".

      I/dalvikvm(1238): hprof: heap dump completed (21354KB)(虛擬機(jī)dumphprof文件)

      D/dalvikvm(1238): GC_HPROF_DUMP_HEAP freed <1K, 13% free 20992K/23879K, external 716K/1038Kpaused 4034ms

      D/AndroidRuntime(1238): Shutting down VM

      W/dalvikvm(1238): threadid=1: thread exiting with uncaught exception (group=0x40015560)

      E/AndroidRuntime(1238): FATAL EXCEPTION: main

      E/AndroidRuntime(1238): java.lang.OutOfMemoryError(是OOM錯誤)

      E/AndroidRuntime(1238): at com.demo.oom.OOMDemoActivity$SomeObj.<init>(OOMDemoActivity.java:45)

      E/AndroidRuntime(1238): at com.demo.oom.OOMDemoActivity.onClick(OOMDemoActivity.java:29)

      E/AndroidRuntime(1238): at android.view.View.performClick(View.java:2485)

      E/AndroidRuntime(1238): at android.view.View$PerformClick.run(View.java:9080)

      E/AndroidRuntime(1238): at android.os.Handler.handleCallback(Handler.java:587)

      E/AndroidRuntime(1238): at android.os.Handler.dispatchMessage(Handler.java:92)

      E/AndroidRuntime(1238): at android.os.Looper.loop(Looper.java:123)

      E/AndroidRuntime(1238): at android.app.ActivityThread.main(ActivityThread.java:3683)

      (從方法堆棧可以看到是應(yīng)用進(jìn)程的主線程中發(fā)生了OOM

      E/AndroidRuntime(1238): at java.lang.reflect.Method.invokeNative(Native Method)

      E/AndroidRuntime(1238): at java.lang.reflect.Method.invoke(Method.java:507)

      E/AndroidRuntime(1238): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)

      E/AndroidRuntime(1238): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)

      E/AndroidRuntime(1238): at dalvik.system.NativeStart.main(Native Method)

      W/ActivityManager(61): Force finishing activity com.demo.oom/.OOMDemoActivity

      D/dalvikvm(229): GC_EXPLICIT freed 8K, 55% free 2599K/5703K, external 716K/1038K, paused 1381ms

      W/ActivityManager(61): Activity pause timeout for HistoryRecord{406671e8 com.demo.oom/.OOMDemoActivity}

      W/ActivityManager(61): Activity destroy timeout for HistoryRecord{406671e8 com.demo.oom/.OOMDemoActivity}

      I/Process(1238): Sending signal. PID: 1238 SIG: 9(錯誤沒有捕獲被拋給虛擬機(jī),最終被kill掉)

      I/ActivityManager(61): Process com.demo.oom (pid 1238) has died.(應(yīng)用進(jìn)程掛掉了)

      獲取hprof文件后再用hprof-conv工具轉(zhuǎn)換一下格式:

      D:\work\android\sdk\tools>hprof-conv.exe C:\Users\ray\Desktop\oom.hprof C:\Users

      \ray\Desktop\oom\oom.hprof(將轉(zhuǎn)換后的hprof放到一個單獨(dú)的目錄下,因?yàn)榉治鰰r會生成很多中間文件)

      2. MAT提供的各種分析工具

      使用MAT導(dǎo)入轉(zhuǎn)換后的hprof文件,導(dǎo)入時會讓你選擇報告類型,選擇“Leak Suspects Report”即可。然后就可以看到如下的初步分析報告:



       

      MATOverview視圖中用餅圖展示了內(nèi)存的使用情況,列出了占用內(nèi)存最大的Java對象com.demo.oom.OOMDemoActivity,我們可以根據(jù)這個線索來繼續(xù)調(diào)查,但如果沒有這樣的提示,也可以根據(jù)自己推斷來分析。在進(jìn)一步分析之前,需要先熟悉MAT為我們提供的各種工具。

      (1) Histogram

      列出每個類的實(shí)例對象的數(shù)量,是第一個非常有用的分析工具。



       

      可以看到該視圖一共有四列,點(diǎn)擊列名可以按照不同的列以升序或降序排序。每一列的含義為:

      Class Name:類名

      Objects:每一種類型的對象數(shù)量

      Shallow Heap:一個對象本身(不包括該對象引用的其他對象)所占用的內(nèi)存

      Retained Heap:一個對象本身,以及由該對象引用的其他對象的Shallow Heap的總和。官方文檔中解釋為:Generally speakingshallow heap of an object is its size in the heap and retained size of the same object is the amount of heap memory that will be freed when the object is garbage collected.

      認(rèn)情況下該視圖是按照Class來分類的,也可以點(diǎn)擊工具欄中的選擇不同的分類類型,這樣可以更方便的篩選需要的信息。

      認(rèn)情況下該視圖只是粗略的計算了每種類型所有對象的Retained Heap,如果要精確計算的話可以點(diǎn)擊工具欄中的來選擇。

      時為了分析進(jìn)程的內(nèi)存使用情況,會對一個在不同的時間點(diǎn)抓取多個hprof文件來觀察,MAT提供了一個非常好的工具來對比這些hprof文件,點(diǎn)擊工具欄中的可以選擇已經(jīng)打開的其他hprof文件,選擇后MAT將會對當(dāng)前的hprof和要對比的hprof做一個插值,這樣就可以很方便的觀察對象的變化了。不過這個工具只有在Histogram視圖中才有。

      列表的第一行是一個搜索框,可以輸入正則式或者數(shù)量來過濾列表的內(nèi)容。



       

                1. (2) Dominator Tree

      列出進(jìn)程中所有的對象,是第二個非常有用的分析工具。



       

      Histogram不同的是左側(cè)列的是對象而不是類(每個對象還有內(nèi)存地址,例如@0x40516b08),而且還多了Percentage一列。

      右鍵點(diǎn)擊任意一個類型,會彈出一個上下文菜單:



       

      菜單中有很多其他非常有用的功能,例如:

      List Objects(with outgoing references/with incoming references):列出由該對象引用的其他對象/引用該對象的其他對象;

      Open Source File:打開該對象的源碼文件;

      Path To GC Roots:由當(dāng)前對象到GC Roots引用鏈

      GC RootsA garbage collection root is an object that is accessible from outside the heap.也就是指那些不會被垃圾回收的對象。圖中標(biāo)識有黃色圓點(diǎn)的對象就是GC Roots,每個GC Root之后都會有灰黑色的標(biāo)識表明這個對象之所以是GC Root的原因。使得一個對象成為GC Root的原因一般有以下幾個

      System Class

      Class loaded by bootstrap/system class loader. For exampleeverything from the rt.jar like java.util.* .

      JNI Local

      Local variable in native code, such as user defined JNI code or JVM internal code.

      JNI Global

      Global variable in native code, such as user defined JNI code or JVM internal code.

      Thread Block

      Object referred to from a currently active thread block.

      Thread

      A started, but not stopped, thread.

      Busy Monitor

      Everything that has called wait() or notify() or that is synchronized. For example, by calling synchronized(Object) or by entering a synchronized method. Static method means class, non-static method means object.

      Java Local

      Local variable. For example, input parameters or locally created objects of methods that are still in the stack of a thread.

      Native Stack

      In or out parameters in native codesuch as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots. For example, parameters used for file/network I/O methods or reflection.

      Finalizer

      An object which is in a queue awaiting its finalizer to be run.

      Unfinalized

      An object which has a finalize method, but has not been finalized and is not yet on the finalizer queue.

      Unreachable

      An object which is unreachable from any other root, but has been marked as a root by MAT to retain objects which otherwise would not be included in the analysis.

      Unknown

      An object of unknown root type. Some dumps, such as IBM Portable Heap Dump files, do not have root information. For these dumps the MAT parser marks objects which are have no inbound references or are unreachable from any other root as roots of this type. This ensures that MAT retains all the objects in the dump.

      在上圖的“Path To GC Roots”的菜單中可以選擇排除不同的非強(qiáng)引用組合來篩選到GC Roots的引用鏈,這樣就可以知道有哪些GC Roots直接或間接的強(qiáng)引用著當(dāng)前對象,導(dǎo)致無法釋放了。



       

      (3) Top Consumers

      classpackage分類表示占用內(nèi)存比較多的對象。

      (4) Leak Suspects

      對內(nèi)存泄露原因的簡單分析,列出了可能的懷疑對象,這些對象可以做為分析的線索。

      (5) OQL

      MAT提供了一種叫做對象查詢語言(Object Query LanguageOQL)的工具,方便用于按照自己的規(guī)則過濾對象數(shù)據(jù)。例如想查詢我的Activity的所有對象:

      SELECT * FROM com.demo.oom.OOMDemoActivity

      或者想查詢指定package下的所有對象:

      SELECT * FROM “com.demo.oom.*” (如果使用通配符,需要用引號)

      或者想查詢某一個類及其子類的所有對象:

      SELECT * FROM INSTANCEOF android.app.Activity

      還有很多高級的用法請參考幫助文檔。

      3. 使用MAT分析OOM原因

      熟悉了以上的各種工具,就可以來分析問題原因了。分析的思路有很多。

      思路一:

      首先我們從MAT的提示中得知com.demo.oom.OOMDemoActivity @ 0x40516b08對象占用了非常多的內(nèi)存(Shallow Size: 160 B Retained Size: 18 MB),我們可以在DominatorTree視圖中查找該對象,或者通過OQL直接查詢該類的對象。



       

      按照Retained Heap降序排列,可以知道OOMDemoActivity對象之所以很大是因?yàn)橛幸粋€占用內(nèi)存很大的ArrayList類型的成員變量,而根本原因是這個集合內(nèi)包含了很多1MB以上的SomeObj對象。此時就可以查看代碼中對SomeObj的操作邏輯,查找為什么會有大量SomeObj存在,為什么每個SomeObj都很大。找到問題后想辦法解決,例如對SomeObj的存儲使用SoftReference,或者減小SomeObj的體積,或者發(fā)現(xiàn)是由于SomeObj沒有被正確的關(guān)閉/釋放,或者有其他static的變量引用這SomeObj。

      思路二

      如果MAT沒能給出任何有價值的提示信息,我們可以根據(jù)自己的判斷來查找可以的對象。因?yàn)榘l(fā)生OOM的進(jìn)程是com.demo.oom,可以使用OQL列出該進(jìn)程package的所有對象,然后再查找可疑的對象。對應(yīng)用程序來說,這是非常常用的方法,如下圖。



       

      通過查詢發(fā)現(xiàn)SomeObj的對象數(shù)量特別多,假設(shè)正常情況下對象用完后應(yīng)該立即釋放才對,是什么導(dǎo)致這些對象沒有被釋放呢?通過“Path To GC Roots”的引用鏈可以知道是OOMDemoActivity中的list引用了SomeObj,所以可以考慮SomeObj是否錯誤的被添加進(jìn)了list中,如下圖。



       

      總之,分析的根本目的就是找到那些數(shù)量很大或者體積很大的對象,以及他們被什么樣的GC Roots引用而沒有被釋放,然后再通過檢查代碼邏輯找到問題原因。

       

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多