如何/何时收集处理程序垃圾?
Posted
技术标签:
【中文标题】如何/何时收集处理程序垃圾?【英文标题】:How/when is a Handler garbage collected? 【发布时间】:2011-07-03 05:20:20 【问题描述】:在我的一个类中,我有以下代码:
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 实例都与单个线程和该线程的消息队列相关联
所以如果我理解正确,只要应用程序线程正在运行,处理程序就不会被垃圾收集,对吗?
在我的具体示例中,由于 Handler 是一个匿名内部类,它具有对封闭对象的隐式引用以及它所指向的对象的整个层次结构。这在我看来就像是内存泄漏的秘诀。
顺便说一句,我可以让处理程序停止发送消息(这就是为什么我有 if (!paused)
)但这不会让它被 GCed,对吧?
那么有没有办法从消息队列中移除 Handler 并让它被 GCed 呢?
【问题讨论】:
【参考方案1】:对 Handler 源代码的检查揭示了更多细节。
以下是 Romain Guy 添加的 Handler() 构造函数中的一些调试代码:
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());
警告很明确:不要将 Handler 子类声明为内部类。
Handler的looper是从一个静态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();
泄漏解剖:
主应用线程保留 Looper 及其 MessageQueue,队列中的 Messages 保留指向其目标 Handler 的链接,并且 Handler——除非它是一个对你的 Activity 有 WeakReference 的静态嵌套类——将保留你的Activity 及其视图。
您可以尝试通过清理您的消息来堵住这个漏洞:
handler.removeMessages(what);
但这说起来容易做起来难。
另见On Memory Leaks in Java and in Android
【讨论】:
所以如果所有消息都应该立即执行,就没有真正的内存泄漏危险吗? 对,如果您有延迟消息,那么它将泄漏。见:***.com/a/11408340/550471【参考方案2】:在我的具体示例中,由于 Handler 是一个匿名内部类,它具有对封闭对象的隐式引用以及它所指向的对象的整个层次结构。
您可以通过使用static
嵌套类而不是匿名内部类将潜在泄漏的影响降低到几乎没有。
【讨论】:
谢谢,这是最好的答案,实际上解决了我的问题。但我仍然认为它是 Android 的设计弱点,如果一旦创建 Handler 就无法删除它。【参考方案3】:不,停止发送消息不会使 GC 工作。正如文档指出的那样,它绑定到创建它的线程。如果线程正在运行,则处理程序不会被 GC 回收。
为什么您认为这会导致内存泄漏? “隐式引用封闭对象”是什么意思?
【讨论】:
@Sarstine - 任何非静态内部类都有对其封闭类实例的隐式引用,因此它可以使用封闭类的实例方法和属性。 没错,隐式引用确实使一些内存保持活动状态,但这不是内存泄漏。外部对象保存在内存中,因为内部对象仍然可以使用它;实际上,如果您不需要它,请将内部类声明为静态的。最后,外部类仍然可以访问。如果外部类无法访问并且仍保留在内存中,这将是内存泄漏。例如,通过一些循环引用,这就是孤立岛。垃圾收集器已经可以处理这种情况了。 就我而言,这是内存泄漏,因为我不再使用外部对象,实际上我想摆脱它。问题是 Handler 仍然有对它的引用,而 Thread 有对 Handler 的引用,没有办法摆脱后者的引用(除非我原来的问题有答案)。以上是关于如何/何时收集处理程序垃圾?的主要内容,如果未能解决你的问题,请参考以下文章