免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


 

說(shuō)明:

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

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

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

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

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

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

  • 此處已 system_process 進(jìn)程為例,在我的測(cè)試環(huán)境中 system_process 進(jìn)程所占用的內(nèi)存的data object Total Size 正常情況下會(huì)穩(wěn)定在 2.2~2.8 之間,而當(dāng)其值超過(guò) 3.55 后進(jìn)程就會(huì)被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)問(wèn)題的代碼片段,最終找到問(wèn)題所在呢?如果從頭到尾的分析代碼邏輯,那肯定會(huì)把人逼瘋,特別是在維護(hù)別人寫的代碼的時(shí)候。這里介紹一個(gè)極好的內(nèi)存分析工具 -- Memory Analyzer Tool(MAT)

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

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

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

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

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

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

1. 生成hprof 文件

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

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

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

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

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

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

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

注意: (4)步和第(5)步能夠正常使用前提是我們需要有 sdcard,并且當(dāng)前進(jìn)程有向 sdcard中寫入的權(quán)限(WRITE_EXTERNAL_STORAGE),否則.hprof 文件不會(huì)被生成,在 logcat 中會(huì)顯示諸如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

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

xxxButton.setOnClickListener(new View.OnClickListener() {

        public void onClick(View view) {

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

                ... ...

        }

}

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

如果不確定進(jìn)程什么時(shí)候會(huì)OOM,例如我們?cè)谂?/span>Monkey的過(guò)程中出現(xiàn)了OOM,此時(shí)最好的辦法就是讓程序在出現(xiàn)OOM之后,而沒有將OOM的錯(cuò)誤信息拋給虛擬機(jī)之前就將進(jìn)程的hprof抓取出來(lái)。方法也很簡(jiǎn)單,只需要在代碼中你認(rèn)為會(huì)拋出OutOfMemoryError的地方try...catch,并在catch塊中使用android.os.Debug.dumpHprofData(String file)方法就可以請(qǐng)求虛擬機(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的文件路徑時(shí),需要考慮權(quán)限問(wèn)題,包括SD卡訪問(wèn)權(quán)限、/data分區(qū)私有目錄訪問(wèn)權(quán)限。

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

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

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

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

(2) 如 果 eclipse 自 動(dòng) 生 成 的 .hprof 文 件 不 能 被 MAT 直 接 打 開 , 或 者 是 使 用android.os.Debug.dumpHprofData()方法手動(dòng)生成的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)換過(guò)后的文件。轉(zhuǎn)換過(guò)后的文件自動(dòng)放在/ANDROID_SDK/tools 目錄下。OK,到此為止,hprof 文件處理完畢,可以用來(lái)分析內(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 會(huì)自動(dòng)解析并生成報(bào)告,點(diǎn)擊 Dominator Tree,并按 Package分組,選擇自己所定義的 Package 類點(diǎn)右鍵,在彈出菜單中選擇 List objects->With incoming references。這時(shí)會(huì)列出所有可疑類,右鍵點(diǎn)擊某一項(xiàng),并選擇 Path to GC Roots -> exclude weak/soft references,會(huì)進(jìn)一步篩選出跟程序相關(guān)的所有有內(nèi)存泄露的類。據(jù)此,可以追蹤到代碼中的某一個(gè)產(chǎn)生泄露的類。

MAT 的界面如下圖所示。



 

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

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

2.3.3 MAT使用方法

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

首先需要構(gòu)建一個(gè)演示程序,并獲取hprof文件。程序很簡(jiǎn)單,按下Button后就循環(huán)地new自定義對(duì)象SomeObj,并將對(duì)象addArrayList中,直到拋出OutOfMemoryError,此時(shí)會(huì)捕獲該錯(cuò)誤,同時(shí)使用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)限。

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

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

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 <1K13% free 20992K/23879K, external 716K/1038K, paused 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錯(cuò)誤)

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 8K55% 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(錯(cuò)誤沒有捕獲被拋給虛擬機(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放到一個(gè)單獨(dú)的目錄下,因?yàn)榉治鰰r(shí)會(huì)生成很多中間文件)

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

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



 

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

(1) Histogram

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



 

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

Class Name:類名

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

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

Retained Heap:一個(gè)對(duì)象本身,以及由該對(duì)象引用的其他對(duì)象的Shallow Heap的總和。官方文檔中解釋為:Generally speaking, shallow 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來(lái)分類的,也可以點(diǎn)擊工具欄中的選擇不同的分類類型,這樣可以更方便的篩選需要的信息。

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

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

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



 

            1. (2) Dominator Tree

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



 

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

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



 

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

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

Open Source File:打開該對(duì)象的源碼文件;

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

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

System Class

Class loaded by bootstrap/system class loader. For example, everything 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 exampleby 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 code, such 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 exampleparameters 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 dumpssuch 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)引用組合來(lái)篩選到GC Roots的引用鏈,這樣就可以知道有哪些GC Roots直接或間接的強(qiáng)引用著當(dāng)前對(duì)象,導(dǎo)致無(wú)法釋放了。



 

(3) Top Consumers

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

(4) Leak Suspects

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

(5) OQL

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

SELECT * FROM com.demo.oom.OOMDemoActivity

或者想查詢指定package下的所有對(duì)象:

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

或者想查詢某一個(gè)類及其子類的所有對(duì)象:

SELECT * FROM INSTANCEOF android.app.Activity

還有很多高級(jí)的用法請(qǐng)參考幫助文檔。

3. 使用MAT分析OOM原因

熟悉了以上的各種工具,就可以來(lái)分析問(wèn)題原因了。分析的思路有很多。

思路一:

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



 

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

思路二

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



 

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



 

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

 

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
用IBM HeapAnalyzer和MOD4J分析Java內(nèi)存泄漏
Android 內(nèi)存泄漏調(diào)試
Android內(nèi)存泄漏分析及調(diào)試
ANDROID 探究oom內(nèi)幕
Android App定位和規(guī)避內(nèi)存泄露方法研究
Android開發(fā)——android調(diào)試工具集【轉(zhuǎn)】
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服