第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開(kāi)發(fā)者教程

Android Studio 編輯器教程

首頁(yè) 慕課教程 Android Studio 編輯器教程 Android Studio 編輯器教程 Android Studio 如何分析內(nèi)存活動(dòng)

Android Studio 如何分析內(nèi)存活動(dòng)

前面的小節(jié)我們學(xué)習(xí)了如何分析 CPU 活動(dòng)。本小節(jié)學(xué)習(xí)如何分析內(nèi)存活動(dòng)。

1. 什么是 Memory Profiler

1.1 Memory Profiler 概覽

Memory Profiler 是 Android Profiler 中的一個(gè)組件,可幫助我們識(shí)別可能會(huì)導(dǎo)致應(yīng)用卡頓、凍結(jié)甚至崩潰的內(nèi)存泄露和內(nèi)存抖動(dòng)。它顯示一個(gè)應(yīng)用內(nèi)存使用量的實(shí)時(shí)圖表,讓我們可以捕獲堆轉(zhuǎn)儲(chǔ)、強(qiáng)制執(zhí)行垃圾回收以及跟蹤內(nèi)存分配。

如果我們的應(yīng)用分配內(nèi)存的速度比系統(tǒng)回收內(nèi)存的速度快,則當(dāng)回收器釋放足夠的內(nèi)存以滿足我們的分配需要時(shí),我們的應(yīng)用可能會(huì)延遲。此延遲可能會(huì)導(dǎo)致我們的應(yīng)用跳幀,并使系統(tǒng)明顯變慢。如果存在內(nèi)存泄露,則即使應(yīng)用在后臺(tái)運(yùn)行也會(huì)保留該內(nèi)存。此行為會(huì)強(qiáng)制執(zhí)行不必要的垃圾回收事件,因而拖慢系統(tǒng)其余部分的內(nèi)存性能。

為幫助防止這些問(wèn)題,我們應(yīng)使用 Memory Profiler 執(zhí)行以下操作:

  • 在時(shí)間軸上查找可能會(huì)導(dǎo)致性能問(wèn)題的不理想的內(nèi)存分配模式;

  • 轉(zhuǎn)儲(chǔ) Java 堆以查看在任何給定時(shí)間哪些對(duì)象耗盡了內(nèi)存。在很長(zhǎng)一段時(shí)間內(nèi)進(jìn)行多次堆轉(zhuǎn)儲(chǔ)有助于識(shí)別內(nèi)存泄露;

  • 記錄正常用戶交互和極端用戶交互期間的內(nèi)存分配,以準(zhǔn)確識(shí)別我們的代碼在何處短時(shí)間內(nèi)分配了過(guò)多對(duì)象,或分配了泄露的對(duì)象。

1.2 打開(kāi) Memory Profiler

要打開(kāi) Memory Profiler,請(qǐng)按以下步驟操作:

依次點(diǎn)擊 View > Tool Windows > Profiler,可以點(diǎn)擊工具欄中的 Profile 圖標(biāo)。

Android Profiler 工具欄中選擇要分析的設(shè)備和應(yīng)用進(jìn)程。

點(diǎn)擊 MEMORY 時(shí)間軸上的任意位置以打開(kāi) Memory Profiler

當(dāng)我們首次打開(kāi) Memory Profiler 時(shí),我們將看到一條表示應(yīng)用內(nèi)存使用量的詳細(xì)時(shí)間軸,并可使用各種工具來(lái)強(qiáng)制執(zhí)行垃圾回收、捕獲堆轉(zhuǎn)儲(chǔ)以及記錄內(nèi)存分配。

  1. 用于強(qiáng)制執(zhí)行垃圾回收事件的按鈕;

  2. 用于捕獲堆轉(zhuǎn)儲(chǔ)的按鈕;

  3. 用于指定分析器多久捕獲一次內(nèi)存分配的下拉菜單。選擇適當(dāng)?shù)倪x項(xiàng)可幫助我們?cè)诜治鰰r(shí)提高應(yīng)用性能;

  4. 用于縮放時(shí)間軸的按鈕;

  5. 用于跳轉(zhuǎn)到實(shí)時(shí)內(nèi)存數(shù)據(jù)的按鈕;

  6. 事件時(shí)間軸,顯示活動(dòng)狀態(tài)、用戶輸入事件和屏幕旋轉(zhuǎn)事件;

  7. 內(nèi)存使用量時(shí)間軸,它會(huì)顯示以下內(nèi)容:

    • 一個(gè)堆疊圖表,顯示每個(gè)內(nèi)存類(lèi)別當(dāng)前使用多少內(nèi)存,如左側(cè)的 y 軸以及頂部的彩色鍵所示;

    • 一條虛線,表示分配的對(duì)象數(shù),如右側(cè)的 y 軸所示;

    • 每個(gè)垃圾回收事件的圖標(biāo)。

2. 如何計(jì)算內(nèi)存

我們?cè)?Memory Profiler 頂部看到的數(shù)字基于我們的應(yīng)用根據(jù) Android 系統(tǒng)機(jī)制所提交的所有私有內(nèi)存頁(yè)面。此計(jì)數(shù)不包含與系統(tǒng)或其他應(yīng)用共享的頁(yè)面。

內(nèi)存計(jì)數(shù)中的類(lèi)別如下:

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

  • Native:從 C 或 C++ 代碼分配的對(duì)象的內(nèi)存;

  • Graphics:圖形緩沖區(qū)隊(duì)列向屏幕顯示像素(包括 GL 表面、GL 紋理等等)所使用的內(nèi)存;(請(qǐng)注意,這是與 CPU 共享的內(nèi)存,不是 GPU 專(zhuān)用內(nèi)存。)

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

  • Code:我們的應(yīng)用用于處理代碼和資源(如 dex 字節(jié)碼、經(jīng)過(guò)優(yōu)化或編譯的 dex 代碼、.so 庫(kù)和字體)的內(nèi)存;

  • Others:我們的應(yīng)用使用的系統(tǒng)不確定如何分類(lèi)的內(nèi)存;

  • Allocated:我們的應(yīng)用分配的 Java/Kotlin 對(duì)象數(shù)。此數(shù)字沒(méi)有計(jì)入 C 或 C++ 中分配的對(duì)象。

3. 內(nèi)存分配

3.1 如何查看內(nèi)存分配

內(nèi)存分配為我們顯示內(nèi)存中的每個(gè) Java 對(duì)象和 JNI 引用是如何分配的。具體而言,Memory Profiler 可為我們顯示有關(guān)對(duì)象分配的以下信息:

  • 分配了哪些類(lèi)型的對(duì)象以及它們使用多少空間;

  • 每個(gè)分配的堆棧軌跡,包括在哪個(gè)線程中;

  • 對(duì)象在何時(shí)被取消分配。

如果我們的設(shè)備搭載的是 Android 8.0 或更高版本,我們可以隨時(shí)查看對(duì)象分配,具體操作步驟如下:在時(shí)間軸上拖動(dòng)以選擇要查看哪個(gè)區(qū)域的分配。不需要開(kāi)始記錄會(huì)話,因?yàn)?Android 8.0 及更高版本附帶設(shè)備內(nèi)置分析工具,可持續(xù)跟蹤我們的應(yīng)用分配。

如果我們的設(shè)備搭載的是 Android 7.1 或更低版本,請(qǐng)點(diǎn)擊 Memory Profiler 工具欄中的 Record memory allocations 圖標(biāo)。記錄時(shí),Memory Profiler 會(huì)跟蹤我們的應(yīng)用中發(fā)生的所有分配。完成后,請(qǐng)點(diǎn)擊 Stop recording 圖標(biāo)以查看分配。

3.2 檢查分析分配記錄

選擇時(shí)間軸的某個(gè)區(qū)域后(或者使用搭載 Android 7.1 或更低版本的設(shè)備完成記錄會(huì)話后),已分配對(duì)象的列表將顯示在時(shí)間軸下方,按類(lèi)名稱進(jìn)行分組,并按其堆計(jì)數(shù)排序。

要檢查分配記錄,請(qǐng)按以下步驟操作:

  1. 瀏覽列表以查找堆計(jì)數(shù)異常大且可能存在泄露的對(duì)象。為幫助查找已知類(lèi),點(diǎn)擊 Class Name 列標(biāo)題以按字母順序排序。然后,點(diǎn)擊一個(gè)類(lèi)名稱。此時(shí)右側(cè)將出現(xiàn) Instance View 窗格,顯示該類(lèi)的每個(gè)實(shí)例;

  2. Instance View 窗格中,點(diǎn)擊一個(gè)實(shí)例。此時(shí)下方將出現(xiàn) Call Stack 標(biāo)簽頁(yè),顯示該實(shí)例被分配到何處以及在哪個(gè)線程中;

  3. Call Stack 標(biāo)簽頁(yè)中,右鍵點(diǎn)擊任意行并選擇 Jump to Source,以在編輯器中打開(kāi)該代碼。

我們可以使用已分配對(duì)象列表上方的兩個(gè)菜單來(lái)選擇要檢查的堆以及如何組織數(shù)據(jù)。從左側(cè)的菜單中,選擇要檢查的堆:

  • default heap:當(dāng)系統(tǒng)未指定堆時(shí);

  • image heap:系統(tǒng)啟動(dòng)映像,包含啟動(dòng)期間預(yù)加載的類(lèi)。此處的分配保證絕不會(huì)移動(dòng)或消失;

  • zygote heap:寫(xiě)時(shí)復(fù)制堆,其中的應(yīng)用進(jìn)程是從 Android 系統(tǒng)中派生的;

  • app heap:我們的應(yīng)用在其中分配內(nèi)存的主堆;

  • JNI heap:顯示 Java 原生接口 (JNI) 引用被分配和釋放到什么位置的堆。

從右側(cè)的菜單中,選擇如何安排分配:

  • Arrange by class:根據(jù)類(lèi)名稱對(duì)所有分配進(jìn)行分組。這是默認(rèn)選項(xiàng);

  • Arrange by package:根據(jù)軟件包名稱對(duì)所有分配進(jìn)行分組;

  • Arrange by callstack:將所有分配分組到其對(duì)應(yīng)的調(diào)用堆棧;

3.2 查看全局 JNI 引用

JNI 引用由原生代碼進(jìn)行管理,因此原生代碼使用的 Java 對(duì)象可能會(huì)保持活動(dòng)狀態(tài)太長(zhǎng)時(shí)間。如果丟棄了 JNI 引用而未先明確將其刪除,Java 堆上的某些對(duì)象可能會(huì)變得無(wú)法訪問(wèn)。此外,還可能會(huì)達(dá)到全局 JNI 引用限制。

要排查此類(lèi)問(wèn)題,請(qǐng)使用 Memory Profiler 中的 JNI heap 視圖來(lái)瀏覽所有全局 JNI 引用,并按 Java 類(lèi)型和原生調(diào)用堆棧對(duì)其進(jìn)行過(guò)濾。借助此信息,我們可以了解創(chuàng)建和刪除全局 JNI 引用的時(shí)間和位置。

在我們的應(yīng)用運(yùn)行時(shí),選擇我們要檢查的一部分時(shí)間軸,然后從類(lèi)列表上方的下拉菜單中選擇 JNI heap。 我們隨后可以像往常一樣檢查堆中的對(duì)象,還可以雙擊 Allocation Call Stack 標(biāo)簽頁(yè)中的對(duì)象,以查看在代碼中將 JNI 引用分配和釋放到了什么位置,如下圖所示。

Tips:要檢查應(yīng)用的 JNI 代碼的內(nèi)存分配,必須將應(yīng)用部署到搭載 Android 8.0 或更高版本的設(shè)備上。

4. 堆轉(zhuǎn)儲(chǔ)

堆轉(zhuǎn)儲(chǔ)顯示在我們捕獲堆轉(zhuǎn)儲(chǔ)時(shí)我們的應(yīng)用中哪些對(duì)象正在使用內(nèi)存。特別是在長(zhǎng)時(shí)間的用戶會(huì)話后,堆轉(zhuǎn)儲(chǔ)會(huì)顯示我們認(rèn)為不應(yīng)再位于內(nèi)存中卻仍在內(nèi)存中的對(duì)象,從而幫助識(shí)別內(nèi)存泄露。

捕獲堆轉(zhuǎn)儲(chǔ)后,我們可以查看以下信息:

  • 我們的應(yīng)用分配了哪些類(lèi)型的對(duì)象,以及每種對(duì)象有多少;

  • 每個(gè)對(duì)象當(dāng)前使用多少內(nèi)存;

  • 在代碼中的什么位置保持著對(duì)每個(gè)對(duì)象的引用;

  • 對(duì)象所分配到的調(diào)用堆棧。

4.1 如何捕獲堆轉(zhuǎn)儲(chǔ)

要捕獲堆轉(zhuǎn)儲(chǔ),請(qǐng)點(diǎn)擊 Memory Profiler 工具欄中的 Dump Java heap 圖標(biāo)。 在轉(zhuǎn)儲(chǔ)堆期間,Java 內(nèi)存量可能會(huì)暫時(shí)增加。 這很正常,因?yàn)槎艳D(zhuǎn)儲(chǔ)與我們的應(yīng)用發(fā)生在同一進(jìn)程中,并需要一些內(nèi)存來(lái)收集數(shù)據(jù)。

堆轉(zhuǎn)儲(chǔ)出現(xiàn)在內(nèi)存時(shí)間軸下方,顯示堆中的所有類(lèi)類(lèi)型,如下圖所示。

在類(lèi)列表中,我們可以查看以下信息:

  • Allocations:堆中的分配數(shù);

  • Native Size:此對(duì)象類(lèi)型使用的原生內(nèi)存總量(以字節(jié)為單位)。只有在使用 Android 7.0 及更高版本時(shí),才會(huì)看到此列;

  • Shallow Size:此對(duì)象類(lèi)型使用的 Java 內(nèi)存總量(以字節(jié)為單位);

  • Retained Size:為此類(lèi)的所有實(shí)例而保留的內(nèi)存總大?。ㄒ宰止?jié)為單位);

點(diǎn)擊一個(gè)類(lèi)名稱可在右側(cè)打開(kāi) Instance View 窗口。列出的每個(gè)實(shí)例都包含以下信息:

  • Depth:從任意 GC 根到選定實(shí)例的最短跳數(shù);

  • Native Size:原生內(nèi)存中此實(shí)例的大小。 只有在使用 Android 7.0 及更高版本時(shí),才會(huì)看到此列;

  • Shallow Size:Java 內(nèi)存中此實(shí)例的大小;

  • Retained Size:此實(shí)例所支配內(nèi)存的大小;

要檢查應(yīng)用的堆,請(qǐng)按以下步驟操作:

  1. 瀏覽列表以查找堆計(jì)數(shù)異常大且可能存在泄露的對(duì)象。為幫助查找已知類(lèi),點(diǎn)擊 Class Name 列標(biāo)題以按字母順序排序。然后,點(diǎn)擊一個(gè)類(lèi)名稱。此時(shí)右側(cè)將出現(xiàn) Instance View 窗格,顯示該類(lèi)的每個(gè)實(shí)例;

  2. Instance View 窗格中,點(diǎn)擊一個(gè)實(shí)例。此時(shí)下方將出現(xiàn) References 標(biāo)簽頁(yè),顯示對(duì)該對(duì)象的每個(gè)引用;

  3. References 標(biāo)簽頁(yè)中,如果我們發(fā)現(xiàn)某個(gè)引用可能在泄露內(nèi)存,請(qǐng)右鍵點(diǎn)擊它并選擇 Go to Instance。這樣會(huì)從堆轉(zhuǎn)儲(chǔ)中選擇相應(yīng)的實(shí)例,從而向我們顯示它自己的實(shí)例數(shù)據(jù)。

4.2 HPROF 文件

捕獲堆轉(zhuǎn)儲(chǔ)后,只有在 Memory Profiler 正在運(yùn)行時(shí),才能在該分析器中查看數(shù)據(jù)。當(dāng)我們退出分析會(huì)話時(shí),會(huì)丟失堆轉(zhuǎn)儲(chǔ)。因此,如果我們要保存堆轉(zhuǎn)儲(chǔ)以供日后查看,請(qǐng)將其導(dǎo)出到 HPROF 文件。

Sessions 窗格中每個(gè) Heap Dump 條目的右側(cè)都有一個(gè) Export Heap Dump 按鈕。在隨即顯示的 Export As 對(duì)話框中,使用 .hprof 文件擴(kuò)展名保存文件。

要使用其他 HPROF 分析器(如 jhat),我們需要將 HPROF 文件從 Android 格式轉(zhuǎn)換為 Java SE HPROF 格式。 我們可以使用 android_sdk/platform-tools/ 目錄中提供的 hprof-conv 工具執(zhí)行此操作。運(yùn)行包含兩個(gè)參數(shù)(即原始 HPROF 文件和轉(zhuǎn)換后 HPROF 文件的寫(xiě)入位置)的 hprof-conv 命令。例如:

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

5. 小結(jié)

本節(jié)課程我們主要學(xué)習(xí)了如何分析內(nèi)存活動(dòng)。本節(jié)課程的重點(diǎn)如下:

  • 掌握如何查看內(nèi)存分配和堆;
  • 掌握如何檢查分析內(nèi)存數(shù)據(jù)。