分析Android應用中內存泄漏的技巧

一、什麼是內存泄漏

內存泄漏(Memory Leak)是指一塊已經被開闢的堆內存由於某種原因程序未釋放或無法釋放,使得這部分內存長時間不能被使用,最終導致程序運行速度減慢甚至崩潰。

Android應用中的內存泄漏是指在應用中申請的內存空間,沒有被程序釋放,一直存在於內存當中,最終導致應用卡頓、崩潰等問題。內存泄漏往往難以發現,但是一旦發現,一定要及時修復,否則成倍的內存泄漏最終會導致應用的崩潰。

二、如何發現內存泄漏

Android提供了一些工具,如內存分析器,幫助我們檢測Android應用中的內存泄漏。

在Android Studio中,我們可以使用Android Profiler來檢測內存泄漏。在Profier中,可以查看應用的內存使用情況、資源使用情況和網絡數據傳輸等信息。其中,在內存選項卡中,可以分別查看Java Heap、Native Heap和GPU Memory的情況,以及Allocation Tracker和Heap Dump的信息。

//使用LeakCanary監測內存泄漏:
dependencies {
   debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
}
public class ExampleApplication extends Application {
  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}

三、內存泄漏的常見場景

1. 對象持有

當一個Activity結束時,如果這個Activity中包含的控件仍被其他地方所持有,這個Activity所佔用的內存就無法釋放。比如當一個Activity持有了一些Bitmap,並將其傳遞給了其他對象使用,但是沒有及時trim內存或者內存不足時,這些Bitmap就會導致內存泄漏。

public class ExampleActivity extends Activity {
  private ImageView mImageView;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_example);
    mImageView = findViewById(R.id.image);
    // 使用了R.drawable.large_image這張大圖
    mImageView.setImageResource(R.drawable.large_image);
  }
}

在以上代碼中,如果這張大圖沒有及時回收,這將會導致內存一直暴增,程序異常。

2. 靜態變量持有對象

靜態變量是不能被GC回收的,如果靜態變量持有了Activity等Context對象,那麼在Activity被回收之前,GC將不能回收Activity所佔用的內存。

以下代碼演示了靜態變量引用了Activity,從而導致Activity不能正常實現回收,API 21及以上的系統機制,可將該Activity加入「歷史記錄列表」,便可被回收。

public class ExampleActivity extends Activity {
    private static Activity sCurrentActivity; 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);
        sCurrentActivity = this;     
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        sCurrentActivity = null;   
    }
}

3. Handler導致泄露

Handler類在Android開發中經常被用來做一些異步的操作,帶有延時的Runnable容易導致內存被泄漏。

public class ExampleActivity extends Activity {
  private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // do some thing;
    }
  };
  
  private void postSomeThingDelay() {
    mHandler.postDelayed(new Runnable() {
      @Override
      public void run() {
      }
    }, 2000);
  }
}

在以上代碼中,如果Activity被銷毀,則handler將長期持有它,並阻止它被回收。

4. 監聽器導致泄露

監聽器是一個常用的功能,在Android中有諸如setOnClickListener、addTextChangedListener等監聽器。如果一個對象把它自己傳給第三方,並註冊一個監聽器,那麼這個對象將不能被GC回收。

public class ExampleActivity extends Activity {
  private EditText editText;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    editText = findViewById(R.id.edit_text);
    editText.addTextChangedListener(new TextWatcher() {
      @Override
      public void beforeTextChanged(CharSequence s, int start, int count, int after) {
      }
      @Override
      public void onTextChanged(CharSequence s, int start, int before, int count) {
      }
      @Override
      public void afterTextChanged(Editable s) {
      }
    });
  }
}

在以上代碼中,如果EditText對象被銷毀之前,內存泄漏將被固定。

四、解決內存泄漏

發現內存泄漏後,要儘快進行解決,以下是幾點解決方案。

1. 及時釋放對象持有的資源

在Activity的onStop()或onDestroy()中,需要釋放那些在onCreate()中創建的資源。如Image、Bitmap等。

2. 定期調用System.gc()

System.gc()的作用是告訴GC執行送達,但並不強制GC立刻執行,GC並不是萬能的,它需要系統去為其提供垃圾收集的條件。所以並不建議調用。

3. 使用WeakReference和軟引用

使用這兩種引用類型可以避免長時間持有Activity對象,當對象沒有其他引用時,會被GC自動回收。

public class ExampleActivity extends Activity {
  private WeakReference mContext;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mContext = new WeakReference(this);
  }

  private void doSomething() {
    if (mContext.get() != null) {
      // do something
    }
  }
}

4. 使用HandlerThread

將線程和Handler分離使用,這樣Handler執行結束時,Thread不會立刻結束。

public class ExampleActivity extends Activity {

  private HandlerThread mHandlerThread;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mHandlerThread = new HandlerThread("MyHandlerThread");
    mHandlerThread.start();
    Handler mHandler = new Handler(mHandlerThread.getLooper()) {
      @Override public void handleMessage(Message msg) {
        // do some thing
      }
    };
  }

  @Override protected void onDestroy() {
    super.onDestroy();
    mHandlerThread.quit();
  }
}

5. 使用LeakCanary進行定位

LeakCanary是一個強大的Android庫,可以在應用內部發現內存泄漏。其會在弱引用對象被回收時,記錄下調用堆棧(如果發現調用堆棧在不斷增大,那麼很可能存在泄漏),當我們檢測到一個對象已經被泄露時,可以通過查看堆轉儲文件,查找泄露原因。

總結

內存泄漏是Android開發中常見的問題,對應的解決方案也有很多,但是依然需要開發者掌握一些技巧才能更好地避免內存泄漏。多檢查自己的代碼,適時使用各種工具進行檢測和解決。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/295430.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-27 12:56
下一篇 2024-12-27 12:56

相關推薦

  • 使用vscode建立UML圖的實踐和技巧

    本文將重點介紹在使用vscode在軟件開發中如何建立UML圖,並且給出操作交互和技巧的指導。 一、概述 在軟件開發中,UML圖是必不可少的重要工具之一。它為軟件架構和各種設計模式的…

    編程 2025-04-29
  • Python創建分配內存的方法

    在python中,我們常常需要創建並分配內存來存儲數據。不同的類型和數據結構可能需要不同的方法來分配內存。本文將從多個方面介紹Python創建分配內存的方法,包括列表、元組、字典、…

    編程 2025-04-29
  • Python變量在內存中的存儲

    該文章將從多個方面對Python變量在內存中的存儲進行詳細闡述,包括變量的聲明和賦值、變量的引用和指向、內存地址的變化、內存管理機制等。 一、聲明和賦值 在Python中,變量聲明…

    編程 2025-04-29
  • 優秀周記1000字的撰寫思路與技巧

    優秀周記是每個編程開發工程師記錄自己工作生活的最佳方式之一。本篇文章將從周記的重要性、撰寫思路、撰寫技巧以及周記的示例代碼等角度進行闡述。 一、周記的重要性 作為一名編程開發工程師…

    編程 2025-04-28
  • Python計算內存佔用

    Python是一種高級的、解釋性的、面向對象的、動態的程序語言,因其易於學習、易於閱讀、可移植性好等優點,越來越受到開發者的青睞。當我們編寫Python代碼時,可能經常需要計算程序…

    編程 2025-04-28
  • 使用Go-Redis獲取Redis集群內存使用率

    本文旨在介紹如何使用Go-Redis獲取Redis集群的內存使用率。 一、Go-Redis簡介 Go-Redis是一個用於連接Redis服務器的Golang客戶端。它支持Redis…

    編程 2025-04-28
  • Android ViewPager和ScrollView滑動衝突問題

    Android開發中,ViewPager和ScrollView是兩個常用的控件。但是當它們同時使用時,可能會發生滑動衝突的問題。本文將從多個方面介紹解決Android ViewPa…

    編程 2025-04-28
  • Android如何點擊其他區域收起軟鍵盤

    在Android應用中,當輸入框獲取焦點彈出軟鍵盤後,我們希望能夠點擊其他區域使軟鍵盤消失,以提升用戶體驗。本篇文章將說明如何實現這一功能。 一、獲取焦點並顯示軟鍵盤 在Andro…

    編程 2025-04-28
  • 堆疊圖配色技巧分享

    堆疊圖是數據可視化中常用的一種表現形式,而配色則是影響堆疊圖觀感和傳達信息的重要因素之一。本文將分享一些堆疊圖配色的技巧,幫助你創造更好的數據可視化。 一、色彩搭配原則 色彩是我們…

    編程 2025-04-27
  • 使用uring_cmd提高開發效率的技巧

    對於編程開發工程師來說,提高效率一直是致力追求的目標。本文將深度解析如何使用uring_cmd,提升工作效率。 一、常用命令 uring_cmd是一個非常強大的命令行工具,但是大部…

    編程 2025-04-27

發表回復

登錄後才能評論