Android 內存泄露與解決方案

內存泄露

Java內存泄漏指的是進程中某些對象(垃圾對象)已經沒有使用價值了,但是它們卻可以直接或間接地引用到gc roots導致無法被GC回收。無用的對象佔據着內存空間,使得實際可使用內存變小,形象地說法就是內存泄漏了。下面分析一些可能導致內存泄漏的情景。

  • 非靜態內部類的靜態實例容易造成內存泄漏
  • activity使用靜態成員
  • handler 非靜態
  • 註冊某個對象後未反註冊
  • 資源對象沒關閉造成的內存泄露
  • 構造Adapter時,沒有使用緩存的 convertView

以上分析只是常見的問題,本質上是
* 全局進程(process-global)的static變量。這個無視應用的狀態,持有Activity的強引用的怪物
* 活在Activity生命週期之外的線程。沒有清空對Activity的強引用
可以參考下面文章的講解及解決方式。本文重點是關注handler這類非靜態引起及使用WeakReference, SoftReference來處理。

[譯]Android內存泄漏的八種可能(上)

[譯]Android防止內存泄漏的八種方法(下)

引例

先看 下面代碼

public class SampleActivity extends Activity {

  private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for 10 minutes.
    mLeakyHandler.postDelayed(new Runnable() {
      @Override
      public void run() { /* ... */ }
    }, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
  }
}

Activity的銷燬會到10分鐘後的handler執行完畢。解決辦法其實有比較多的方案,這裏使用WeakReference

解決方案

public class SampleActivity extends Activity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }

  private final MyHandler mHandler = new MyHandler(this);

  /**
   * Instances of anonymous classes do not hold an implicit
   * reference to their outer class when they are "static".
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
  }
}

這樣一來Activity不再是handler的強引用了,對於重度Activity類如此來解決還是比較好的。
Finally understanding how references work in Android and Java講到了WeakReference, SoftReference,PhantomReference,Strong reference.一般SoftReference更適合做Cache,在系統內存不足時纔會清理。

Strong reference: strong references are the ordinary references in Java. Anytime we create a new object, a strong reference is by default created.

WeakReference: a weak reference is a reference not strong enough to keep the object in memory. If we try to determine if the object is strongly referenced and it happened to be through WeakReferences, the object will be garbage-collected.

SoftReference: think of a SoftReference as a stronger WeakReference. Whereas a WeakReference will be collected immediately, a SoftReference will beg to the GC to stay in memory unless there is no other option. The Garbage Collector algorithms are really thrilling and something you can dive in for hours and hours without getting tired. But basically, they say “I will always reclaim the WeakReference. If the object is a SoftReference, I will decide what to do based on the ecosystem conditions”. This makes a SoftReference very useful for the implementation of a cache: as long as the memory is plenty, we do not have to worry of manually removing objects.

PhantomReference: Ah, PhantomReferences! I think I can count on the fingers of one hand how often I saw them used in a production environment. An Object that has only being referenced through a PhantomReference them can be collected whenever the Garbage Collector wants. No further explanations, no “call me back”. This makes it hard to characterise. Why would we like to use such a thing? Are the other ones not problematic enough? Why did I choose to be a programmer? PhantomReference can be used exactly to detect when an object has been removed from memory. Being fully transparent, I recall two occasions when I had to use a PhantomReference in my entire professional career. So do not get stressed if they are hard to understand now.

參考鏈接


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章