2011-02-24 50 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