天天看點

記憶體洩露和記憶體溢出,并不是那麼簡單

1.記憶體溢出oom

程式發生OMM并不表示RAM不足,而是因為程式申請的java heap對象超過了dalvik vm heapgrowthlimit。也就是說,在RAM充足的情況下,也可能發生OOM。

java程式發生OMM并不是表示RAM不足,如果RAM真的不足,會發生什麼呢?這時Android的memory killer會起作用,當RAM所剩不多時,memory killer會殺死一些優先級比較低的程序來釋放實體記憶體,讓高優先級程式得到更多的記憶體

這個是因為Android系統對dalvik的vm heapsize作了硬性限制,當java程序申請的java空間超過門檻值時,就會抛出OOM異常(這個門檻值可以是48M、24M、16M等,視機型而定),可以通過adb shell getprop | grep dalvik.vm.heapgrowthlimit檢視此值。

也就是說,程式發生OMM并不表示RAM不足,而是因為程式申請的java heap對象超過了dalvik vm heapgrowthlimit。也就是說,在RAM充足的情況下,也可能發生OOM.

即發生記憶體洩露,是oom的主要源頭。

2.記憶體洩露

Java記憶體洩漏指的是程序中某些對象(垃圾對象)已經沒有使用價值了,但是它們卻可以直接或間接地引用到gc roots導緻無法被GC回收。無用的對象占據着記憶體空間,使得實際可使用記憶體變小,形象地說法就是記憶體洩漏了.記憶體洩露的原因以及解決方法有:

2.1非靜态内部類的靜态執行個體容易造成記憶體洩漏

 對于lauchMode不是singleInstance的Activity, 應該避免在activity裡面執行個體化其非靜态内部類的靜态執行個體。

2.2activity使用靜态成員

·不要對activity的context長期引用(一個activity的引用的生存周期應該和activity的生命周期相同)

·如果可以的話,盡量使用關于application的context來替代和activity相關的context

·如果一個acitivity的非靜态内部類的生命周期不受控制,那麼避免使用它;正确的方法是使用一個靜态的内部類,并且對它的外部類有一WeakReference,就像在ViewRootImpl中内部類W所做的那樣。

2.3使用handler時的記憶體問題

This Handler class should be static or leaks might occur (com.example.ta.HandlerActivity.1)
           

可以将handler變為static ,或者添加弱引用

public class SampleActivity extends Activity {

  /**
   * 使用靜态的内部類,不會持有目前對象的引用
   */
  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);

  /**
   * 使用靜态的内部類,不會持有目前對象的引用
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { }
  };

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

    //  發送一個10分鐘後執行的一個消息
    mHandler.postDelayed(sRunnable, 600000);

    // 結束
    finish();
  }
}
           

因為HandlerThread實作的run方法是一個無限循環,它不會自己結束,線程的生命周期超過了activity生命周期,當橫豎屏切換,HandlerThread線程的數量會随着activity重建次數的增加而增加。

應該在onDestroy時将線程停止掉:mThread.getLooper().quit();

另外,對于不是HandlerThread的線程,也應該確定activity消耗後,線程已經終止,可以這樣做:在onDestroy時調用mThread.join();

2.4注冊某個對象後未取消注冊

 注冊廣播接收器、注冊觀察者等等,當它不用時,及時unregister

2.5集合中對象沒清理造成的記憶體洩露

2.6資源對象沒關閉造成的記憶體洩露

 對檔案,資源進行調用,cursor等,當資源對象加載完成,應該及時colse();

參考文檔:http://blog.csdn.net/gemmem/article/details/13017999