時間線縮放控件 控制底部時間戳顯示間距。
當檢視某個時間段記憶體情況時,可通過這個按鈕回到實時預覽。
紫色的點是顯示 Activity 狀态、使用者輸入 Event 和螢幕旋轉 Event 的 Event 時間線
(1)**右側上半部分:**TOTOL JAVA NATIVE GRAPHICS…
Java:從 Java 或 Kotlin 代碼配置設定的對象記憶體。
Native:從 C 或 C++ 代碼配置設定的對象記憶體。
Native:即使您的應用中不使用 C++,您也可能會看到此處使用的一些原生記憶體,因為 Android 架構使用原生記憶體代表您處理各種任務,如處理圖像資源和其他圖形時,即使您編寫的代碼采用 Java 或 Kotlin 語言。
Graphics:圖形緩沖區隊列向螢幕顯示像素(包括 GL 表面、GL 紋理等等)所使用的記憶體。 (請注意,這是與 CPU 共享的記憶體,不是 GPU 專用記憶體。)
Stack: 您的應用中的原生堆棧和 Java 堆棧使用的記憶體。 這通常與您的應用運作多少線程有關。
Code:您的應用用于處理代碼和資源(如 dex 位元組碼、已優化或已編譯的 dex 碼、.so 庫和字型)的記憶體。
Other:您的應用使用的系統不确定如何分類的記憶體。
Allocated:您的應用配置設定的 Java/Kotlin 對象數。 它沒有計入 C 或 C++ 中配置設定的對象。
**注意:**Android 7.1 及更低版本的裝置時,此配置設定僅在 Memory Profiler 連接配接至您運作的應用時才開始計數。 是以,您開始分析之前配置設定的任何對象都不會被計入。 Android 8.0 附帶一個裝置内置分析工具,該工具可記錄所有配置設定,
是以,在 Android 8.0 及更高版本上,此數字始終表示您的應用中待處理的 Java 對象總數。
(2)虛線:
虛線表示配置設定的對象數,如右側的 y 軸所示。
(3)垃圾桶:
用于表示每個垃圾回收 Event 的圖示。
紅色圓圈時我自己畫的:
Android7.0以下版本會有,8.0沒有,這個的作用是記錄記憶體配置設定情況的。7.0以前,需要開始和結束,8.0隻要再時間線上拖動就可以了。(8.0 及更高版本附帶裝置内置分析工具,可持續跟蹤您的應用配置設定。)
注意:*重點内容*
注:在 Android 7.1 及更低版本上,您最多可以記錄 65535 個配置設定。 如果您的記錄會話超出此限值,則記錄中僅儲存最新的 65535 個配置設定。 (在 Android 8.0 及更高版本中,則沒有實際的限制。)
一、profiler能幹嘛
檢視配置設定哪些類型的對象以及它們使用多少空間。
每個配置設定的堆疊追蹤,包括在哪個線程中。
對象在何時被取消配置設定(僅當使用運作 Android 8.0 或更高版本的裝置時)。
點選記憶體展示下方的:arrange by package,找到我們自己的package。選中其中一個執行個體。
Arrange by class:基于類名稱對所有配置設定進行分組。
Arrange by package:基于軟體包名稱對所有配置設定進行分組。
Arrange by callstack:将所有配置設定分組到其對應的調用堆棧。
InstanceView 展示的是我們執行個體的配置設定記憶體時間,和釋放時間
CallStack 展示的是調用的堆棧,看過activity啟動源碼的同學是不是很熟悉呢。
官方文檔說明:檢視調用堆棧,7.0以下,需要手動dump。才能檢視,我用的是8.0的系統。Dump Java heap 就可以。就是我畫的那個紅圈圈左邊的圖示。
操作步驟:先force GC點選一下垃圾桶,這樣有利用于分析記憶體洩漏。
注意:如果您需要更精确地dump java heap。
import android.os.Debug;
調用Debug.dumpHprofData(“”)
或者在需要的地方:
Debug.startMethodTracing(“”);
//function…
Debug.stopMethodTracing();
都會在指定目錄生成一個hprof檔案。
分析記憶體洩漏:
Android 提供一個托管記憶體環境,當它确定您的應用不再使用某些對象時,垃圾回收器會将未使用的記憶體釋放回堆中。系統都必須在某個時間點短暫地暫停您的代碼。 大多數情況下,這些暫停難以察覺。
這是官方給出的一些記憶體方面的建議:包括如何減少apk大小,背景服務的優化
https://developer.android.com/topic/performance/memory
一些:需要注意的:
1.JobScheduler
2.nano protobufs:是一種與語言無關,平台無關的可擴充機制,由Google設計用于序列化結構化資料 - 類似于XML,但更小,更快,更簡單
3.使用優化的資料容器
4.Dagger 2:Dagger不使用反射掃描您的應用程式代碼。Dagger的靜态編譯時實作.
意味着它可以在Android應用程式中使用,而無需不必要的運作時成本或記憶體使用。
5.for建立過多對象。在ondraw中,建立paint或者bitmap。
貼出了維基百科中的工廠模式:
https://en.wikipedia.org/wiki/Factory_method_pattern
下面來說一些如何檢測記憶體洩漏:
步驟:先手動force GC 畫紅圈圈最坐标的那個圖示。點選dump java heap。
這時,studio會自動截取一定時間段的記憶體片段。可以點選儲存按鈕(下圖左邊那個綠箭頭)将這些檔案儲存下來。擴充名.hprof。然後用studio File open 打開。擴充名别打錯。
儲存的.prof檔案也可以用其他工具檢視,mat,jhat。
方法:将 HPROF 檔案從 Android 格式轉換為 Java SE HPROF 格式。 使用 android_sdk/platform-tools/ hprof-conv
hprof-conv a/b/c/heap-original.hprof h/g/iheap-converted.hprof 指定對目錄就可以了。一個輸入一個輸出。就可以用别的工具打開了。
1.我們可以通過Total Count(總執行個體數)和Heap Count(堆記憶體中執行個體數)
2.然後在下方的Reference Tree中我們可以看到目前這個執行個體對象持有的具體的對象,那麼在這一部分我們怎麼排查呢?我們主要需要關注的是 Dominating Size(目前指向的這個一條,在記憶體中占有的大小)值最大的前面幾條,為什麼呢?因為洩漏導緻記憶體無法被釋放值越大,存在洩漏的可能性越大。
3.在Analyzer中有一個功能就是 Detect Leaked Activities,點選綠色三角按鈕運作後,可以幫我們分析出目前可能存在洩漏的Activity對象。
4.Reference Tree根據裡邊的堆棧資訊一步步點開。找到有下面圖檔标記綠色箭頭的那個标記。就看确定了。
這就是沒被回收的執行個體,以及引用,還有調用的堆棧資訊。右鍵有選項可以調到代碼位置。
名詞的說明:
在類清單中,您可以檢視以下資訊:
Heap Count:堆中的執行個體數。
Shallow Size:此堆中所有執行個體的總大小(以位元組為機關)。
Retained Size:為此類的所有執行個體而保留的記憶體總大小(以位元組為機關)。
在 Instance View 中,每個執行個體都包含以下資訊:
Depth:從任意 GC 根到所選執行個體的最短 hop 數。了解為引用深度。
Shallow Size:此執行個體的大小。
Retained Size:此執行個體支配的記憶體大小(根據 dominator 樹)。
Retained Size大體可以了解為:gc之後如果不洩漏能,釋放的大小。
這是正常的activity
import android.content.Intent;
import android.os.Debug;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//try {
// Debug.dumpHprofData("");
//} catch (IOException e) {
// e.printStackTrace();
//}
//Debug.startMethodTracing("");
//Debug.stopMethodTracing();
}
public void btnClick(View v){
startActivity(new Intent(this, SecondActivity.class));
}
}
context被單利引用的:SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Singleton.getInstance(this);
Button button = new Button(this);
button.setLayoutParams(new FrameLayout.LayoutParams(, ));
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
setContentView(button);
}
}
官方給出的建議:
使用 Memory Profiler 時,可以跑 monkeyr unner 并嘗試強制記憶體洩漏。 在應用中引發記憶體洩漏的一種方式是,先讓其運作一段時間,然後再檢查堆。 洩漏在堆中可能逐漸彙聚到配置設定頂部。 不過,洩漏越小,您越需要運作更長時間的應用才能看到洩漏。
您還可以通過以下方式之一觸發記憶體洩漏:
将裝置從縱向旋轉為橫向,然後在不同的 Activity 狀态下反複操作多次。
旋轉裝置經常會導緻應用洩漏 Activity、Context 或 View 對象,因為系統會重新建立 Activity,而如果您的應用在其他地方保持對這些對象之一的引用,系統将無法對其進行垃圾回收。
處于不同的 Activity 狀态時,在您的應用與另一個應用之間切換(導航到主螢幕,然後傳回到您的應用)。
記憶體洩漏情況及解決辦法:
長時間引用 Activity、Context、View、Drawable 和其他對象,可能會保持對 Activity 或 Context 容器的引用。
可以保持 Activity 執行個體的非靜态内部類,如 Runnable。
對象保持時間超出所需時間的緩存。
1.static變量引起的記憶體洩漏 因為static變量的生命周期是在類加載時開始 類解除安裝時結束,也就是說static變量是在程式程序死亡時才釋放,如果在static變量中 引用了Activity 那麼 這個Activity由于被引用,便會随static變量的生命周期一樣,一直無法被釋放,造成記憶體洩漏。
解決辦法: 在Activity被靜态變量引用時,使用 getApplicationContext 因為Application生命周期從程式開始到結束,和static變量的一樣。
2.線程造成的記憶體洩漏 類似于上述例子中的情況,線程執行時間很長,及時Activity跳出還會執行,因為線程或者Runnable是Acticvity内部類,是以握有Activity的執行個體(因為建立内部類必須依靠外部類),是以造成Activity無法釋放。 AsyncTask 有線程池,問題更嚴重
解決辦法: 1.合理安排線程執行的時間,控制線程在Activity結束前結束。 2.将内部類改為靜态内部類,并使用弱引用WeakReference來儲存Activity執行個體 因為弱引用 隻要GC發現了 就會回收它 ,是以可盡快回收
3.BitMap占用過多記憶體 bitmap的解析需要占用記憶體,但是記憶體隻提供8M的空間給BitMap,如果圖檔過多,并且沒有及時 recycle bitmap 那麼就會造成記憶體溢出。
解決辦法: 及時recycle 壓縮圖檔之後加載圖檔
4.資源未被及時關閉造成的記憶體洩漏 比如一些Cursor 沒有及時close 會儲存有Activity的引用,導緻記憶體洩漏
解決辦法: 在onDestory方法中及時 close即可
5.Handler的使用造成的記憶體洩漏 由于在Handler的使用中,handler會發送message對象到 MessageQueue中 然後 Looper會輪詢MessageQueue 然後取出Message執行,但是如果一個Message長時間沒被取出執行,那麼由于 Message中有 Handler的引用,而 Handler 一般來說也是内部類對象,Message引用 Handler ,Handler引用 Activity 這樣 使得 Activity無法回收。
解決辦法: 依舊使用 靜态内部類+弱引用的方式 可解決
這裡做一個擴充:
dumpsys
可以提供有關系統服務的資訊。
dumpsys -h 可以檢視支援的功能
dumpsys -l 列出能dumpsys的 service
如:
dumpsys activity -h 可以繼續檢視dumpsys activity 能用的指令。可以分析activity跳轉時,棧中的情況。舉例說明。
以上都是在adb shell 下運作。
不知道寫的好不好,都是自己學的東西。希望我們大家可以多溝通溝通有什麼介意。可以寫在下面。一起進步。