1. 程式人生 > >使用Handler容易產生的記憶體洩露以及介紹下Java的4種引用

使用Handler容易產生的記憶體洩露以及介紹下Java的4種引用

最近時間都利用的不太好,都是到下午才開始學習或者做事,一上午都吹B或者XXX用掉了。。。不太好,這裡督促下自己不要再懶惰,哈哈!!

廢話不多說,今天來講下一個“經常”遇到的一個記憶體洩露的情況來引出想提的Java的4種引用方式

在舉例子之前先將一些基礎的知識,不然基礎薄弱的同學還會不理解什麼的。(當然,我會講的很粗略,但是你一定能看懂)

通常Java分配記憶體有三種策略,分別為 靜態儲存區

靜態儲存區: 平時那些static的變數啊,方法啊都在這裡面,並且在程式整個執行期間都存在。

:我們執行的那些方法所產生的物件都在這裡面,方法結束這些東西就會被釋放掉。

:我們平時 new出來的那些物件都會在這裡面。

那如何區分堆,棧呢?

區域性變數的基本資料型別和引用儲存於棧中,引用的物件實體儲存於堆中。—— 因為它們屬於方法中的變數,生命週期隨方法而結束。

成員變數全部儲存於堆中(包括基本資料型別,引用和引用的物件實體)—— 因為它們屬於類,類物件終究是要被new出來使用的。

那接下來我們來看下例子

Handler 造成的記憶體洩漏

為什麼會出現?

Handler 屬於 TLS(Thread Local Storage) 變數, 生命週期和 Activity 是不一致的。所以很有可能我們的Activity的邏輯早就結束了,但是Handler持有Activity的引用導致Activity的資源無法回收。

可以看下這個簡單的例子:

public classWjjActivityextendsActivity {

private final Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
  // 你的業務行為
}
}

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

// 強行延遲操作
myHandler.postDelayed(new
Runnable() { @Override public void run() { /* ... */ } }, 1000 * 60 * 20); finish(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

myHandler 將其 push 進了訊息佇列 MessageQueue 裡。 當該 Activity 被 finish() 掉時,延遲執行任務的 Message 還會繼續存在於主執行緒中,它持有該 Activity 的 Handler 引用,所以此時 finish() 掉的 Activity 就不會被回收了 從而造成記憶體洩漏(因 Handler 為非靜態內部類,它會持有外部類的引用)。

那我們可以在Handler申明的時候加 static,讓他存活期跟 Activity 的生命週期就無關了。

然後我又找到了一個更好的解決方法,就是通過弱引用的方式引入 Activity,避免直接將 Activity 作為 context 傳進去。

public classSampleActivityextendsActivity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */
  private static classMyHandlerextendsHandler {
    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();
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

這樣如果Activity已經不在了,也就不做這些事了。

上面已經用到了弱飲用,那麼和強引用又有什麼區別呢?就是接下來要講的。

java的引用有4種分別是 強引用(StrongReference)軟引用(SoftReference)弱引用(WeakReference)虛引用(PhantomReference)

強引用(StrongReference)

強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。 就是我們平時 new 的那些物件(這裡不包括區域性變數,別誤解)

像這樣

Apple a=new Apple();
  • 1

如果是方法內的區域性變數,在方法結束前你也不需要做把他設定為null的操作,讓GC來消除他。顯式地設定a為null,或超出物件的生命週期範圍,則gc認為該物件不存在引用,這時就可以回收這個物件

像list之類的因為 你通常是 List list =new ArrayList<>();出來了的,所以如果不用了,建議手動clear()下,而不是以為list.get(x)為null了他就會被回收。

軟引用(SoftReference)

如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。


String v=new String("wjj");          // 強引用  
SoftReference<String> softRef=new SoftReference<String>(v); // 軟引用    
  • 1
  • 2
  • 3

那麼在記憶體吃緊的時候softRef就會被回收。

使用場景: 我有一個Activity,他有很多圖片,圖片都在map裡快取,我切出去再back回來如果不用快取,每次IO操作去讀sd卡,但是有了快取我就可以迅速的載入,提升使用者的體驗。如果記憶體吃緊被回收了,那也只能去重新載入了(強烈抗議刻意不讓系統回收這些快取資料)

弱引用(WeakReference)

在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。

和軟引用的區別就是,一個記憶體不足才回收,一個看到就回收!

用法跟上面幾乎一樣,就不貼程式碼了。

應用推薦: 如果這個物件是偶爾的使用,並且希望在使用時隨時就能獲取到,但又不想影響此物件的垃圾收集,那麼你應該用 Weak Reference 來記住此物件。

虛引用(PhantomReference)

如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。

大致的概念如下

這裡寫圖片描述

因為一些個人原因,後面幾天我都會處於休假狀態,整理的東西會停更幾天,恢復工作狀態後我會補上吧,提前預祝大家 5.1愉快!

這裡寫圖片描述

部分知識源於網上,Tx for 簫鑑哥