Android性能優(yōu)化:Memory Profiler

Memory Profiler 是 Android Profiler 中的一個組件何址,可幫助你分析應(yīng)用卡頓吨灭、崩潰系馆、內(nèi)存泄漏等原因。 它顯示一個應(yīng)用內(nèi)存使用量的實時圖表搂蜓,讓您可以捕獲堆轉(zhuǎn)儲狼荞、強(qiáng)制執(zhí)行垃圾回收以及跟蹤內(nèi)存分配。

為什么應(yīng)分析您的應(yīng)用內(nèi)存

Android 提供一個托管內(nèi)存環(huán)境—當(dāng)它確定您的應(yīng)用不再使用某些對象時帮碰,垃圾回收器會將未使用的內(nèi)存釋放回堆中相味。 雖然 Android 查找未使用內(nèi)存的方式在不斷改進(jìn),但對于所有 Android 版本殉挽,系統(tǒng)都必須在某個時間點短暫地暫停您的代碼丰涉。 大多數(shù)情況下,這些暫停難以察覺斯碌。 不過一死,如果您的應(yīng)用分配內(nèi)存的速度比系統(tǒng)回收內(nèi)存的速度快,則當(dāng)收集器釋放足夠的內(nèi)存以滿足您的分配需要時傻唾,您的應(yīng)用可能會延遲投慈。 此延遲可能會導(dǎo)致您的應(yīng)用跳幀,并使系統(tǒng)明顯變慢冠骄。

盡管您的應(yīng)用不會表現(xiàn)出變慢伪煤,但如果存在內(nèi)存泄漏,則即使應(yīng)用在后臺運行也會保留該內(nèi)存凛辣。 此行為會強(qiáng)制執(zhí)行不必要的垃圾回收 Event抱既,因而拖慢系統(tǒng)的內(nèi)存性能。 最后扁誓,系統(tǒng)被迫終止您的應(yīng)用進(jìn)程以回收內(nèi)存防泵。 然后阳堕,當(dāng)用戶返回您的應(yīng)用時,它必須完全重啟择克。

為幫助防止這些問題恬总,您應(yīng)使用 Memory Profiler 執(zhí)行以下操作:

  • 在時間線中查找可能會導(dǎo)致性能問題的不理想的內(nèi)存分配模式。
  • 轉(zhuǎn)儲 Java 堆以查看在任何給定時間哪些對象耗盡了使用內(nèi)存肚邢。 長時間進(jìn)行多個堆轉(zhuǎn)儲可幫助識別內(nèi)存泄漏壹堰。
  • 記錄正常用戶交互和極端用戶交互期間的內(nèi)存分配以準(zhǔn)確識別您的代碼在何處短時間分配了過多對象,或分配了泄漏的對象骡湖。
    如需了解可減少應(yīng)用內(nèi)存使用的編程做法贱纠,請閱讀管理您的應(yīng)用內(nèi)存

Memory Profiler 概覽

當(dāng)您首次打開 Memory Profiler 時响蕴,您將看到一條表示應(yīng)用內(nèi)存使用量的詳細(xì)時間線谆焊,并可訪問用于強(qiáng)制執(zhí)行垃圾回收、捕捉堆轉(zhuǎn)儲和記錄內(nèi)存分配的各種工具浦夷。


圖 1. Memory Profiler

如圖 1 所示辖试,Memory Profiler 的默認(rèn)視圖包括以下各項:

  1. 用于強(qiáng)制執(zhí)行垃圾回收 Event 的按鈕。
  2. 用于捕獲堆轉(zhuǎn)儲的按鈕
  3. 用于記錄內(nèi)存分配情況的按鈕劈狐。 此按鈕僅在連接至運行 Android 7.1 或更低版本的設(shè)備時才會顯示罐孝。
  4. 用于放大/縮小時間線的按鈕。
  5. 用于跳轉(zhuǎn)至實時內(nèi)存數(shù)據(jù)的按鈕肥缔。
  6. Event 時間線莲兢,其顯示 Activity 狀態(tài)、用戶輸入 Event 和屏幕旋轉(zhuǎn) Event续膳。
  7. 內(nèi)存使用量時間線改艇,其包含以下內(nèi)容:
    • 一個顯示每個內(nèi)存類別使用多少內(nèi)存的堆疊圖表,如左側(cè)的 y 軸以及頂部的彩色鍵所示坟岔。
    • 虛線表示分配的對象數(shù)谒兄,如右側(cè)的 y 軸所示。
    • 用于表示每個垃圾回收 Event 的圖標(biāo)炮车。

不過舵变,如果您使用的是運行 Android 7.1 或更低版本的設(shè)備,則默認(rèn)情況下瘦穆,并不是所有分析數(shù)據(jù)均可見纪隙。 如果您看到一條消息,其顯示“Advanced profiling is unavailable for the selected process”扛或,則需要啟用高級分析以查看下列內(nèi)容:

  • Event 時間線
  • 分配的對象數(shù)
  • 垃圾回收 Event

在 Android 8.0 及更高版本上绵咱,始終為可調(diào)試應(yīng)用啟用高級分析。

如何計算內(nèi)存

您在 Memory Profiler(圖 2)頂部看到的數(shù)字取決于您的應(yīng)用根據(jù) Android 系統(tǒng)機(jī)制所提交的所有私有內(nèi)存頁面數(shù)熙兔。 此計數(shù)不包含與系統(tǒng)或其他應(yīng)用共享的頁面悲伶。


圖 2. Memory Profiler 頂部的內(nèi)存計數(shù)圖例

內(nèi)存計數(shù)中的類別如下所示:

  • Java:從 Java 或 Kotlin 代碼分配的對象內(nèi)存艾恼。
  • Native:從 C 或 C++ 代碼分配的對象內(nèi)存。
    即使您的應(yīng)用中不使用 C++麸锉,您也可能會看到此處使用的一些原生內(nèi)存钠绍,因為 Android 框架使用原生內(nèi)存代表您處理各種任務(wù),如處理圖像資源和其他圖形時花沉,即使您編寫的代碼采用 Java 或 Kotlin 語言柳爽。
  • Graphics:圖形緩沖區(qū)隊列向屏幕顯示像素(包括 GL 表面、GL 紋理等等)所使用的內(nèi)存碱屁。 (請注意磷脯,這是與 CPU 共享的內(nèi)存,不是 GPU 專用內(nèi)存娩脾。)
  • Stack: 您的應(yīng)用中的原生堆棧和 Java 堆棧使用的內(nèi)存赵誓。 這通常與您的應(yīng)用運行多少線程有關(guān)。
  • Code:您的應(yīng)用用于處理代碼和資源(如 dex 字節(jié)碼柿赊、已優(yōu)化或已編譯的 dex 碼俩功、.so 庫和字體)的內(nèi)存。
  • Other:您的應(yīng)用使用的系統(tǒng)不確定如何分類的內(nèi)存闹瞧。
  • Allocated:您的應(yīng)用分配的 Java/Kotlin 對象數(shù)绑雄。 它沒有計入 C 或 C++ 中分配的對象展辞。
    當(dāng)連接至運行 Android 7.1 及更低版本的設(shè)備時奥邮,此分配僅在 Memory Profiler 連接至您運行的應(yīng)用時才開始計數(shù)。 因此罗珍,您開始分析之前分配的任何對象都不會被計入洽腺。 不過,Android 8.0 附帶一個設(shè)備內(nèi)置分析工具覆旱,該工具可記錄所有分配蘸朋,因此,在 Android 8.0 及更高版本上扣唱,此數(shù)字始終表示您的應(yīng)用中待處理的 Java 對象總數(shù)藕坯。

與以前的 Android Monitor 工具中的內(nèi)存計數(shù)相比,新的 Memory Profiler 以不同的方式記錄您的內(nèi)存噪沙,因此炼彪,您的內(nèi)存使用量現(xiàn)在看上去可能會更高些。 Memory Profiler 監(jiān)控的類別更多正歼,這會增加總的內(nèi)存使用量辐马,但如果您僅關(guān)心 Java 堆內(nèi)存,則“Java”項的數(shù)字應(yīng)與以前工具中的數(shù)值相似局义。

然而喜爷,Java 數(shù)字可能與您在 Android Monitor 中看到的數(shù)字并非完全相同冗疮,這是因為應(yīng)用的 Java 堆是從 Zygote 啟動的,而新數(shù)字則計入了為它分配的所有物理內(nèi)存頁面檩帐。 因此术幔,它可以準(zhǔn)確反映您的應(yīng)用實際使用了多少物理內(nèi)存。

注:目前湃密,Memory Profiler 還會顯示應(yīng)用中的一些誤報的原生內(nèi)存使用量特愿,而這些內(nèi)存實際上是分析工具使用的。 對于大約 100000 個對象勾缭,最多會使報告的內(nèi)存使用量增加 10MB揍障。 在這些工具的未來版本中,這些數(shù)字將從您的數(shù)據(jù)中過濾掉俩由。

查看內(nèi)存分配

內(nèi)存分配顯示內(nèi)存中每個對象是如何分配的毒嫡。 具體而言,Memory Profiler 可為您顯示有關(guān)對象分配的以下信息:

  • 分配哪些類型的對象以及它們使用多少空間幻梯。
  • 每個分配的堆疊追蹤兜畸,包括在哪個線程中。
  • 對象在何時被取消分配(僅當(dāng)使用運行 Android 8.0 或更高版本的設(shè)備時)碘梢。

如果您的設(shè)備運行 Android 8.0 或更高版本咬摇,您可以隨時按照下述方法查看您的對象分配: 只需點擊并按住時間線,并拖動選擇您想要查看分配的區(qū)域(如圖 3中所示)煞躬。 不需要開始記錄會話肛鹏,因為 Android 8.0 及更高版本附帶設(shè)備內(nèi)置分析工具,可持續(xù)跟蹤您的應(yīng)用分配恩沛。


圖 3. 對于Android 8.0 及更高版本在扰,選擇一個現(xiàn)有時間線區(qū)域以查看對象分配

使用 Memory Profiler 查看 Java 堆和內(nèi)存分配

Memory Profiler 是 Android Profiler 中的一個組件,可幫助您識別導(dǎo)致應(yīng)用卡頓雷客、凍結(jié)甚至崩潰的內(nèi)存泄漏和流失芒珠。 它顯示一個應(yīng)用內(nèi)存使用量的實時圖表,讓您可以捕獲堆轉(zhuǎn)儲搅裙、強(qiáng)制執(zhí)行垃圾回收以及跟蹤內(nèi)存分配皱卓。

要打開 Memory Profiler,請按以下步驟操作:

  1. 點擊 View > Tool Windows > Android Profiler(也可以點擊工具欄中的 Android Profiler [圖片上傳中...(image-2c570a-1533109180178-0)]

    )部逮。

  2. 從 Android Profiler 工具欄中選擇您想要分析的設(shè)備和應(yīng)用進(jìn)程娜汁。 如果您通過 USB 連接了某個設(shè)備但該設(shè)備未在設(shè)備列表中列出,請確保您已啟用 USB 調(diào)試甥啄。

  3. 點擊 **MEMORY **時間線中的任意位置可打開 Memory Profiler存炮。

或者,您可以在命令行中使用 dumpsys 檢查您的應(yīng)用內(nèi)存,同時查看 logcat 中的 GC Event穆桂。

為什么應(yīng)分析您的應(yīng)用內(nèi)存

Android 提供一個托管內(nèi)存環(huán)境—當(dāng)它確定您的應(yīng)用不再使用某些對象時宫盔,垃圾回收器會將未使用的內(nèi)存釋放回堆中。 雖然 Android 查找未使用內(nèi)存的方式在不斷改進(jìn)享完,但對于所有 Android 版本灼芭,系統(tǒng)都必須在某個時間點短暫地暫停您的代碼。 大多數(shù)情況下般又,這些暫停難以察覺彼绷。 不過,如果您的應(yīng)用分配內(nèi)存的速度比系統(tǒng)回收內(nèi)存的速度快茴迁,則當(dāng)收集器釋放足夠的內(nèi)存以滿足您的分配需要時寄悯,您的應(yīng)用可能會延遲。 此延遲可能會導(dǎo)致您的應(yīng)用跳幀堕义,并使系統(tǒng)明顯變慢猜旬。

盡管您的應(yīng)用不會表現(xiàn)出變慢,但如果存在內(nèi)存泄漏倦卖,則即使應(yīng)用在后臺運行也會保留該內(nèi)存洒擦。 此行為會強(qiáng)制執(zhí)行不必要的垃圾回收 Event,因而拖慢系統(tǒng)的內(nèi)存性能怕膛。 最后熟嫩,系統(tǒng)被迫終止您的應(yīng)用進(jìn)程以回收內(nèi)存。 然后褐捻,當(dāng)用戶返回您的應(yīng)用時掸茅,它必須完全重啟。

為幫助防止這些問題舍扰,您應(yīng)使用 Memory Profiler 執(zhí)行以下操作:

  • 在時間線中查找可能會導(dǎo)致性能問題的不理想的內(nèi)存分配模式倦蚪。
  • 轉(zhuǎn)儲 Java 堆以查看在任何給定時間哪些對象耗盡了使用內(nèi)存。 長時間進(jìn)行多個堆轉(zhuǎn)儲可幫助識別內(nèi)存泄漏边苹。
  • 記錄正常用戶交互和極端用戶交互期間的內(nèi)存分配以準(zhǔn)確識別您的代碼在何處短時間分配了過多對象,或分配了泄漏的對象裁僧。

如需了解可減少應(yīng)用內(nèi)存使用的編程做法个束,請閱讀管理您的應(yīng)用內(nèi)存

Memory Profiler 概覽

當(dāng)您首次打開 Memory Profiler 時聊疲,您將看到一條表示應(yīng)用內(nèi)存使用量的詳細(xì)時間線茬底,并可訪問用于強(qiáng)制執(zhí)行垃圾回收、捕捉堆轉(zhuǎn)儲和記錄內(nèi)存分配的各種工具获洲。

[圖片上傳中...(image-1dc2f9-1533109180179-4)]

圖 1. Memory Profiler

如圖 1 所示阱表,Memory Profiler 的默認(rèn)視圖包括以下各項:

  1. 用于強(qiáng)制執(zhí)行垃圾回收 Event 的按鈕。
  2. 用于捕獲堆轉(zhuǎn)儲的按鈕
  3. 用于記錄內(nèi)存分配情況的按鈕最爬。 此按鈕僅在連接至運行 Android 7.1 或更低版本的設(shè)備時才會顯示涉馁。
  4. 用于放大/縮小時間線的按鈕。
  5. 用于跳轉(zhuǎn)至實時內(nèi)存數(shù)據(jù)的按鈕爱致。
  6. Event 時間線烤送,其顯示 Activity 狀態(tài)、用戶輸入 Event 和屏幕旋轉(zhuǎn) Event糠悯。
  7. 內(nèi)存使用量時間線帮坚,其包含以下內(nèi)容:
    • 一個顯示每個內(nèi)存類別使用多少內(nèi)存的堆疊圖表,如左側(cè)的 y 軸以及頂部的彩色鍵所示互艾。
    • 虛線表示分配的對象數(shù)试和,如右側(cè)的 y 軸所示。
    • 用于表示每個垃圾回收 Event 的圖標(biāo)纫普。

不過灰署,如果您使用的是運行 Android 7.1 或更低版本的設(shè)備,則默認(rèn)情況下局嘁,并不是所有分析數(shù)據(jù)均可見溉箕。 如果您看到一條消息,其顯示“Advanced profiling is unavailable for the selected process”悦昵,則需要啟用高級分析以查看下列內(nèi)容:

  • Event 時間線
  • 分配的對象數(shù)
  • 垃圾回收 Event

在 Android 8.0 及更高版本上肴茄,始終為可調(diào)試應(yīng)用啟用高級分析。

如何計算內(nèi)存

您在 Memory Profiler(圖 2)頂部看到的數(shù)字取決于您的應(yīng)用根據(jù) Android 系統(tǒng)機(jī)制所提交的所有私有內(nèi)存頁面數(shù)但指。 此計數(shù)不包含與系統(tǒng)或其他應(yīng)用共享的頁面寡痰。

[圖片上傳中...(image-a75add-1533109180178-3)]

圖 2. Memory Profiler 頂部的內(nèi)存計數(shù)圖例

內(nèi)存計數(shù)中的類別如下所示:

  • Java:從 Java 或 Kotlin 代碼分配的對象內(nèi)存。

  • Native:從 C 或 C++ 代碼分配的對象內(nèi)存棋凳。

    即使您的應(yīng)用中不使用 C++拦坠,您也可能會看到此處使用的一些原生內(nèi)存,因為 Android 框架使用原生內(nèi)存代表您處理各種任務(wù)剩岳,如處理圖像資源和其他圖形時贞滨,即使您編寫的代碼采用 Java 或 Kotlin 語言。

  • Graphics:圖形緩沖區(qū)隊列向屏幕顯示像素(包括 GL 表面拍棕、GL 紋理等等)所使用的內(nèi)存晓铆。 (請注意,這是與 CPU 共享的內(nèi)存绰播,不是 GPU 專用內(nèi)存骄噪。)

  • Stack: 您的應(yīng)用中的原生堆棧和 Java 堆棧使用的內(nèi)存。 這通常與您的應(yīng)用運行多少線程有關(guān)蠢箩。

  • Code:您的應(yīng)用用于處理代碼和資源(如 dex 字節(jié)碼链蕊、已優(yōu)化或已編譯的 dex 碼事甜、.so 庫和字體)的內(nèi)存。

  • Other:您的應(yīng)用使用的系統(tǒng)不確定如何分類的內(nèi)存滔韵。

  • Allocated:您的應(yīng)用分配的 Java/Kotlin 對象數(shù)逻谦。 它沒有計入 C 或 C++ 中分配的對象。

    當(dāng)連接至運行 Android 7.1 及更低版本的設(shè)備時奏属,此分配僅在 Memory Profiler 連接至您運行的應(yīng)用時才開始計數(shù)跨跨。 因此,您開始分析之前分配的任何對象都不會被計入囱皿。 不過勇婴,Android 8.0 附帶一個設(shè)備內(nèi)置分析工具,該工具可記錄所有分配嘱腥,因此耕渴,在 Android 8.0 及更高版本上,此數(shù)字始終表示您的應(yīng)用中待處理的 Java 對象總數(shù)齿兔。

與以前的 Android Monitor 工具中的內(nèi)存計數(shù)相比橱脸,新的 Memory Profiler 以不同的方式記錄您的內(nèi)存,因此分苇,您的內(nèi)存使用量現(xiàn)在看上去可能會更高些添诉。 Memory Profiler 監(jiān)控的類別更多,這會增加總的內(nèi)存使用量医寿,但如果您僅關(guān)心 Java 堆內(nèi)存栏赴,則“Java”項的數(shù)字應(yīng)與以前工具中的數(shù)值相似。

然而靖秩,Java 數(shù)字可能與您在 Android Monitor 中看到的數(shù)字并非完全相同须眷,這是因為應(yīng)用的 Java 堆是從 Zygote 啟動的,而新數(shù)字則計入了為它分配的所有物理內(nèi)存頁面沟突。 因此花颗,它可以準(zhǔn)確反映您的應(yīng)用實際使用了多少物理內(nèi)存。

注:目前惠拭,Memory Profiler 還會顯示應(yīng)用中的一些誤報的原生內(nèi)存使用量扩劝,而這些內(nèi)存實際上是分析工具使用的。 對于大約 100000 個對象求橄,最多會使報告的內(nèi)存使用量增加 10MB今野。 在這些工具的未來版本中,這些數(shù)字將從您的數(shù)據(jù)中過濾掉罐农。

查看內(nèi)存分配

內(nèi)存分配顯示內(nèi)存中每個對象是如何分配的。 具體而言催什,Memory Profiler 可為您顯示有關(guān)對象分配的以下信息:

  • 分配哪些類型的對象以及它們使用多少空間涵亏。
  • 每個分配的堆疊追蹤,包括在哪個線程中。
  • 對象在何時被取消分配(僅當(dāng)使用運行 Android 8.0 或更高版本的設(shè)備時)气筋。

如果您的設(shè)備運行 Android 8.0 或更高版本拆内,您可以隨時按照下述方法查看您的對象分配: 只需點擊并按住時間線,并拖動選擇您想要查看分配的區(qū)域(如圖3 中所示)宠默。 不需要開始記錄會話麸恍,因為 Android 8.0 及更高版本附帶設(shè)備內(nèi)置分析工具,可持續(xù)跟蹤您的應(yīng)用分配搀矫。


圖 3. 對于Android 8.0 及更高版本抹沪,選擇一個現(xiàn)有時間線區(qū)域以查看對象分配

如果您的設(shè)備運行 Android 7.1 或更低版本,則在 Memory Profiler 工具欄中點擊 Record memory allocations 瓤球。 記錄時融欧,Android Monitor 將跟蹤您的應(yīng)用中進(jìn)行的所有分配。 操作完成后卦羡,點擊 Stop recording 噪馏。
對于 Android 7.1 及更低版本,您必須顯式記錄內(nèi)存分配

在選擇一個時間線區(qū)域后(或當(dāng)您使用運行 Android 7.1 或更低版本的設(shè)備完成記錄會話時)绿饵,已分配對象的列表將顯示在時間線下方欠肾,按類名稱進(jìn)行分組,并按其堆計數(shù)排序拟赊。

注:在 Android 7.1 及更低版本上刺桃,您最多可以記錄 65535 個分配要门。 如果您的記錄會話超出此限值欢搜,則記錄中僅保存最新的 65535 個分配封豪。 (在 Android 8.0 及更高版本中,則沒有實際的限制炒瘟。)

要檢查分配記錄,請按以下步驟操作:

  1. 瀏覽列表以查找堆計數(shù)異常大且可能存在泄漏的對象缘琅。 為幫助查找已知類,點擊 Class Name 列標(biāo)題以按字母順序排序廓推。 然后點擊一個類名稱刷袍。 此時在右側(cè)將出現(xiàn) Instance View 窗格樊展,顯示該類的每個實例堆生,如圖 3 中所示淑仆。
  2. 在 Instance View 窗格中哥力,點擊一個實例。 此時下方將出現(xiàn) Call Stack 標(biāo)簽寞射,顯示該實例被分配到何處以及哪個線程中钞澳。
  3. 在 Call Stack 標(biāo)簽中,點擊任意行以在編輯器中跳轉(zhuǎn)到該代碼策治。
圖 3. 有關(guān)每個已分配對象的詳情顯示在右側(cè)的 Instance View 中通惫。

默認(rèn)情況下混蔼,左側(cè)的分配列表按類名稱排列惭嚣。 在列表頂部,您可以使用右側(cè)的下拉列表在以下排列方式之間進(jìn)行切換:

  • Arrange by class:基于類名稱對所有分配進(jìn)行分組延旧。
  • Arrange by package:基于軟件包名稱對所有分配進(jìn)行分組槽地。
  • Arrange by callstack:將所有分配分組到其對應(yīng)的調(diào)用堆棧捌蚊。

捕獲堆轉(zhuǎn)儲

堆轉(zhuǎn)儲顯示在您捕獲堆轉(zhuǎn)儲時您的應(yīng)用中哪些對象正在使用內(nèi)存。 特別是在長時間的用戶會話后挺智,堆轉(zhuǎn)儲會顯示您認(rèn)為不應(yīng)再位于內(nèi)存中卻仍在內(nèi)存中的對象窗宦,從而幫助識別內(nèi)存泄漏迫摔。 在捕獲堆轉(zhuǎn)儲后,您可以查看以下信息:

  • 您的應(yīng)用已分配哪些類型的對象沪摄,以及每個類型分配多少纱烘。
  • 每個對象正在使用多少內(nèi)存擂啥。
  • 在代碼中的何處仍在引用每個對象。
  • 對象所分配到的調(diào)用堆棧屋吨。 (目前山宾,如果您在記錄分配時捕獲堆轉(zhuǎn)儲资锰,則只有在 Android 7.1 及更低版本中,堆轉(zhuǎn)儲才能使用調(diào)用堆棧直秆。)


    圖 4. 查看堆轉(zhuǎn)儲

要捕獲堆轉(zhuǎn)儲圾结,在 Memory Profiler 工具欄中點擊 Dump Java heap 在轉(zhuǎn)儲堆期間懊缺,Java 內(nèi)存量可能會暫時增加鹃两。 這很正常,因為堆轉(zhuǎn)儲與您的應(yīng)用發(fā)生在同一進(jìn)程中途蒋,并需要一些內(nèi)存來收集數(shù)據(jù)馋记。

堆轉(zhuǎn)儲顯示在內(nèi)存時間線下,顯示堆中的所有類類型宽堆,如圖 4 所示畜隶。

注:如果您需要更精確地了解轉(zhuǎn)儲的創(chuàng)建時間,可以通過調(diào)用 dumpHprofData() 在應(yīng)用代碼的關(guān)鍵點創(chuàng)建堆轉(zhuǎn)儲浸遗。

要檢查您的堆跛锌,請按以下步驟操作:

  1. 瀏覽列表以查找堆計數(shù)異常大且可能存在泄漏的對象届惋。 為幫助查找已知類盼樟,點擊 Class Name 列標(biāo)題以按字母順序排序。 然后點擊一個類名稱译秦。 此時在右側(cè)將出現(xiàn) Instance View 窗格击碗,顯示該類的每個實例稍途,如圖 5 中所示。
  2. 在 Instance View 窗格中突勇,點擊一個實例坷虑。此時下方將出現(xiàn) References迄损,顯示該對象的每個引用。
    或者痊远,點擊實例名稱旁的箭頭以查看其所有字段,然后點擊一個字段名稱查看其所有引用冒版。 如果您要查看某個字段的實例詳情壤玫,右鍵點擊該字段并選擇 Go to Instance。
  3. 在 References 標(biāo)簽中沸呐,如果您發(fā)現(xiàn)某個引用可能在泄漏內(nèi)存班缎,則右鍵點擊它并選擇 Go to Instance达址。 這將從堆轉(zhuǎn)儲中選擇對應(yīng)的實例,顯示您自己的實例數(shù)據(jù)疆虚。
    默認(rèn)情況下径簿,堆轉(zhuǎn)儲不會向您顯示每個已分配對象的堆疊追蹤嘀韧。 要獲取堆疊追蹤锄贷,在點擊 Dump Java heap 之前,您必須先開始記錄內(nèi)存分配柔昼。 然后因惭,您可以在 Instance View 中選擇一個實例蹦魔,并查看 Call Stack 標(biāo)簽以及 References 標(biāo)簽,如圖 5 所示乒躺。不過嘉冒,在您開始記錄分配之前,可能已分配一些對象顶籽,因此礼饱,調(diào)用堆棧不能用于這些對象究驴。 包含調(diào)用堆棧的實例在圖標(biāo)上用一個“堆椚饔牵”標(biāo)志表示。 (遺憾的是榄鉴,由于堆疊追蹤需要您執(zhí)行分配記錄牢硅,因此芝雪,您目前無法在 Android 8.0 上查看堆轉(zhuǎn)儲的堆疊追蹤惩系。)

在您的堆轉(zhuǎn)儲中,請注意由下列任意情況引起的內(nèi)存泄漏:

  • 長時間引用 Activity抒抬、Context擦剑、View、Drawable 和其他對象赚抡,可能會保持對 Activity 或 Context 容器的引用纠屋。
  • 可以保持 Activity 實例的非靜態(tài)內(nèi)部類售担,如 Runnable族铆。
  • 對象保持時間超出所需時間的緩存。
圖 5. 捕獲堆轉(zhuǎn)儲需要的持續(xù)時間標(biāo)示在時間線中

在類列表中炫乓,您可以查看以下信息:

  • Heap Count:堆中的實例數(shù)。
  • Shallow Size:此堆中所有實例的總大写撮稀(以字節(jié)為單位)莽红。
  • Retained Size:為此類的所有實例而保留的內(nèi)存總大小(以字節(jié)為單位)醉蚁。

在類列表頂部鬼店,您可以使用左側(cè)下拉列表在以下堆轉(zhuǎn)儲之間進(jìn)行切換:

  • Default heap:系統(tǒng)未指定堆時妇智。
  • App heap:您的應(yīng)用在其中分配內(nèi)存的主堆巍棱。
  • Image heap:系統(tǒng)啟動映像,包含啟動期間預(yù)加載的類如贷。 此處的分配保證絕不會移動或消失杠袱。
  • Zygote heap:寫時復(fù)制堆,其中的應(yīng)用進(jìn)程是從 Android 系統(tǒng)中派生的谊路。

默認(rèn)情況下缠劝,此堆中的對象列表按類名稱排列骗灶。 您可以使用其他下拉列表在以下排列方式之間進(jìn)行切換:

  • Arrange by class:基于類名稱對所有分配進(jìn)行分組耙旦。
  • Arrange by package:基于軟件包名稱對所有分配進(jìn)行分組免都。
  • Arrange by callstack:將所有分配分組到其對應(yīng)的調(diào)用堆棧。 此選項僅在記錄分配期間捕獲堆轉(zhuǎn)儲時才有效脓规。 即使如此侨舆,堆中的對象也很可能是在您開始記錄之前分配的绢陌,因此這些分配會首先顯示脐湾,且只按類名稱列出沥割。

默認(rèn)情況下,此列表按 Retained Size 列排序帜讲。 您可以點擊任意列標(biāo)題以更改列表的排序方式椒拗。

Instance View 中获黔,每個實例都包含以下信息:

  • Depth:從任意 GC 根到所選實例的最短 hop 數(shù)玷氏。
  • Shallow Size:此實例的大小盏触。
  • Retained Size:此實例支配的內(nèi)存大锌榻取(根據(jù) dominator 樹)授艰。

將堆轉(zhuǎn)儲另存為 HPROF

在捕獲堆轉(zhuǎn)儲后淮腾,僅當(dāng)分析器運行時才能在 Memory Profiler 中查看數(shù)據(jù)。 當(dāng)您退出分析會話時洲押,您將丟失堆轉(zhuǎn)儲诅诱。 因此送朱,如果您要保存堆轉(zhuǎn)儲以供日后查看驶沼,可通過點擊時間線下方工具欄中的 Export heap dump as HPROF file回怜,將堆轉(zhuǎn)儲導(dǎo)出到一個 HPROF 文件中换薄。 在顯示的對話框中轻要,確保使用 .hprof 后綴保存文件冲泥。

然后壁涎,通過將此文件拖到一個空的編輯器窗口(或?qū)⑵渫系轿募?biāo)簽欄中)怔球,您可以在 Android Studio 中重新打開該文件竟坛。

要使用其他 HPROF 分析器(如 jhat)钧舌,您需要將 HPROF 文件從 Android 格式轉(zhuǎn)換為 Java SE HPROF 格式延刘。 您可以使用 android_sdk/platform-tools/ 目錄中提供的 hprof-conv 工具執(zhí)行此操作碘赖。 運行包括以下兩個參數(shù)的 hprof-conv 命令:原始 HPROF 文件和轉(zhuǎn)換后 HPROF 文件的寫入位置普泡。 例如:

hprof-conv heap-original.hprof heap-converted.hprof

分析內(nèi)存的技巧

使用 Memory Profiler 時,您應(yīng)對應(yīng)用代碼施加壓力并嘗試強(qiáng)制內(nèi)存泄漏歧匈。 在應(yīng)用中引發(fā)內(nèi)存泄漏的一種方式是件炉,先讓其運行一段時間斟冕,然后再檢查堆磕蛇。 泄漏在堆中可能逐漸匯聚到分配頂部十办。 不過向族,泄漏越小炸枣,您越需要運行更長時間的應(yīng)用才能看到泄漏。

您還可以通過以下方式之一觸發(fā)內(nèi)存泄漏:

  • 將設(shè)備從縱向旋轉(zhuǎn)為橫向霍衫,然后在不同的 Activity 狀態(tài)下反復(fù)操作多次敦跌。 旋轉(zhuǎn)設(shè)備經(jīng)常會導(dǎo)致應(yīng)用泄漏 Activity柠傍、ContextView 對象惧笛,因為系統(tǒng)會重新創(chuàng)建 Activity,而如果您的應(yīng)用在其他地方保持對這些對象之一的引用拜效,系統(tǒng)將無法對其進(jìn)行垃圾回收紧憾。
  • 處于不同的 Activity 狀態(tài)時赴穗,在您的應(yīng)用與另一個應(yīng)用之間切換(導(dǎo)航到主屏幕般眉,然后返回到您的應(yīng)用)潜支。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市已烤,隨后出現(xiàn)的幾起案子妓羊,更是在濱河造成了極大的恐慌胯究,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躁绸,死亡現(xiàn)場離奇詭異裕循,居然都是意外死亡臣嚣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門剥哑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硅则,“玉大人,你說我怎么就攤上這事怎虫。” “怎么了困介?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵大审,是天一觀的道長。 經(jīng)常有香客問我座哩,道長徒扶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任根穷,我火速辦了婚禮姜骡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缠诅。我一直安慰自己溶浴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布管引。 她就那樣靜靜地躺著士败,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褥伴。 梳的紋絲不亂的頭發(fā)上谅将,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音重慢,去河邊找鬼饥臂。 笑死,一個胖子當(dāng)著我的面吹牛似踱,可吹牛的內(nèi)容都是我干的隅熙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼核芽,長吁一口氣:“原來是場噩夢啊……” “哼囚戚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起轧简,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤驰坊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后哮独,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拳芙,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡察藐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了舟扎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片分飞。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖浆竭,靈堂內(nèi)的尸體忽然破棺而出浸须,到底是詐尸還是另有隱情,我是刑警寧澤邦泄,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布删窒,位于F島的核電站,受9級特大地震影響顺囊,放射性物質(zhì)發(fā)生泄漏肌索。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一特碳、第九天 我趴在偏房一處隱蔽的房頂上張望诚亚。 院中可真熱鬧,春花似錦午乓、人聲如沸站宗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梢灭。三九已至,卻和暖如春蒸其,著一層夾襖步出監(jiān)牢的瞬間敏释,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工摸袁, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留钥顽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓靠汁,卻偏偏與公主長得像蜂大,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝶怔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355