Как/когда собирается мусор Handler?
Внутри моего класса у меня есть следующий код:
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 является анонимным внутренним классом, он имеет неявную ссылку на охватывающий Object и всю иерархию объектов, которые указаны им. Это выглядит как рецепт утечки памяти.
Btw, я могу заставить обработчика прекратить отправку сообщений (почему у меня есть if (!paused)
), но это не сделает его GCed, правильно?
Итак, есть способ удалить обработчик из очереди сообщений и заставить его быть GCed?
Ответы
Ответ 1
В моем конкретном примере, поскольку Handler является анонимным внутренним классом, он имеет неявную ссылку на охватывающий Object и всю иерархию объектов, которые указаны им.
Вы могли бы уменьшить влияние потенциальной утечки почти на ничего, используя static
вложенный класс вместо анонимного внутреннего класса.
Ответ 2
Изучение источника обработчика показывает более подробную информацию.
Вот какой код отладки из конструктора 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 как внутренний класс.
Обработчик петлителя обработчика получается из статического экземпляра 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, сообщения в очереди сохраняют ссылку на свой целевой обработчик, а обработчик - если это не статический вложенный класс с WeakReference для вашей активности - сохранит ваш Деятельность и ее взгляды.
Вместо этого вы можете попытаться подключить эту утечку, очистив сообщения:
handler.removeMessages(what);
но это легче сказать, чем сделать.
Также см. Утечки памяти на Java и в Android
Ответ 3
Нет, прекратить отправку сообщения не делает работу GC. Как указывает док, он ограничивается ниткой, которая ее создает. Если поток запущен, обработчик не будет возвращен GC.
Почему вы думаете, что это может вызвать утечку памяти? Что вы подразумеваете под "неявной ссылкой на вложенный объект"?