2011-02-24 21 views
15

裏面一類礦井我有以下代碼:如何/何時收集處理程序垃圾?

mHandler = createHandler(); 

private Handler createHandler() { 
    return new Handler() { 
     public void handleMessage (Message msg) { 
      update(); 
      if (!paused) { 
       sendEmptyMessageDelayed(0, 300); 
      } 
     } 
    }; 
} 

文檔說:

http://developer.android.com/reference/android/os/Handler.html

每個處理程序實例與單個線程關聯和線程的消息隊列

所以,如果我理解正確的處理程序不是垃圾只要應用程序線程正在運行,就會收集到的信息是否正確?

在我的具體示例中,由於Handler是一個匿名內部類,因此它對封閉對象以及它所指向的對象的整個層次結構具有隱式引用。這看起來像是一個泄漏內存的配方。

順便說一句,我可以讓處理程序停止發送消息(這就是爲什麼我有if (!paused)),但這不會使它被GCed,對吧?

那麼有沒有辦法從消息隊列中刪除處理程序,並讓它被GCed?

回答

3

在我的具體例子中,由於Handler是一個匿名的內部類,它有一個隱含的引用,它包含了對象以及它指向的整個對象的層次結構。

您可以通過使用static嵌套類而不是匿名內部類來減少潛在泄漏對幾乎沒有影響。

+1

謝謝,這是最好的答案,切實解決我的問題。但是,如果在創建Handler後無法刪除Handler,我仍然認爲這是Android的一個設計弱點。 – Roland 2011-02-24 20:18:03

0

不,停止發送消息不會使GC工作。正如文檔指出的那樣,它會綁定到創建它的線程。如果線程正在運行,處理程序將不會被GC回收。

您爲什麼認爲這可能會導致內存泄漏? 「隱含對象的引用」是什麼意思?

+0

@Sarstine - 任何非靜態的內部類都有一個隱含的對它的封閉類的實例的引用,以便它可以使用封閉的類實例方法和屬性。 – 2011-02-24 01:48:43

+0

確實,隱式引用確實保留了一些內存,但這不是內存泄漏。外部對象保存在內存中,因爲內部對象仍然可以使用它;事實上,如果你不需要它,靜態聲明內部類。最後,外部類仍然可以訪問。 如果外部類無法訪問,並且仍保留在內存中,則會導致內存泄漏。例如,通過一些循環引用,這是隔離島。垃圾收集器可以處理這種情況了。 – Sarstime 2011-02-24 02:02:45

+0

在我的情況下,這是一個內存泄漏,因爲我不再有任何用於外部對象,實際上我想擺脫它。問題在於Handler仍然有對它的引用,並且Thread有對Handler的引用,並且沒有辦法擺脫後面的引用(除非對原始問題有答案)。 – Roland 2011-02-24 17:08:39

14

對處理程序源碼的檢查揭示了更多細節。

下面是從處理程序()構造一些調試代碼,是由羅曼蓋伊補充說:

if (FIND_POTENTIAL_LEAKS) { 
    final Class<? extends Handler> klass = getClass(); 
    if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 
     (klass.getModifiers() & Modifier.STATIC) == 0) { 
    Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 
     klass.getCanonicalName()); 
    } 
} 

警告非常明確:不要聲明你的處理程序子類作爲一個內部類。

處理程序的彎針從一個靜態ThreadLocal實例獲得:

mLooper = Looper.myLooper(); 

/** 
* Return the Looper object associated with the current thread. Returns 
* null if the calling thread is not associated with a Looper. 
*/ 
public static final Looper myLooper() { 
    return (Looper)sThreadLocal.get(); 
} 

解剖學泄漏:

主要應用線程保留了活套和其的MessageQueue,在隊列中的消息保留的鏈路到他們的目標處理程序,而處理程序 - 除非它是一個靜態嵌套類,其中包含對您的活動的WeakReference - 將保留您的活動及其視圖。

你可以嘗試,而不是通過清理你的消息,以堵塞這泄漏:

handler.removeMessages(what); 

但這談何容易。

另見On Memory Leaks in Java and in Android

+0

因此,如果所有消息都應該立即執行,那麼不存在內存泄漏的真正危險? – 2012-06-29 08:43:15

+0

對,如果你有延遲的信息,那麼它會泄漏。請參閱:http://stackoverflow.com/a/11408340/550471 – 2012-07-18 19:28:49