Может ли анонимный Handler или Runnable создать утечку памяти?

new Handler().postDelayed(new Runnable(){

@Override
public void run() {
     // do stuff
}}, 100);

Если я вызову это из действия (onCreate или onResume или где-то еще), может ли это вызвать утечку памяти? Я читал, что new Runnable() на самом деле должен быть статическим экземпляром, это правда?


person StackOverflowed    schedule 21.07.2013    source источник
comment
Вы читали, что это должен быть статический класс. Анонимный класс не может быть статическим, поэтому вы не должны использовать его таким образом. Но проблема в нестатичности, а не в анонимности.   -  person user207421    schedule 07.01.2015


Ответы (2)


Да. Этот код может вызвать утечку памяти.

Пока этот анонимный класс, основанный на Runnable, находится в очереди (100 миллисекунд в этом примере), он сохраняет ссылку на внешний класс Activity.

Такая утечка памяти, конечно, не является проблемой сама по себе, но в зависимости от того, какой код внутри run выполняется, это может создать более серьезные проблемы, такие как сбой приложения, когда вы, например, попробуйте отобразить диалог после того, как Activity будет убит. В таких ситуациях вы увидите приятные информационные исключения:

IllegalArgumentException: невозможно выполнить это действие после onSaveInstanceState

or

BadTokenException: невозможно добавить окно - ... ваша активность запущена?

person MaciejGórski    schedule 21.07.2013
comment
Я собирался опубликовать ответ в том же духе. Вы думаете, что удаление обратных вызовов из обработчика решает эту проблему? - person Vikram; 22.07.2013
comment
@викрам Да. Я обычно удаляю Runnable в onPause. - person MaciejGórski; 22.07.2013
comment
Черт, у нас эти ошибки повсюду в крашлитике.. :D - person Nima G; 16.10.2014
comment
@ MaciejGórski Привет, у меня есть вопрос, что теперь GC может обрабатывать рекурсивную зависимость, так почему это проблема? Это потому, что исполняемый (поток) является одним из корневых узлов? - person JaskeyLam; 31.12.2014
comment
Looper основного потока @Jaskey определяется как статический, поэтому это корень, содержащий MessageQueue, который содержит все Message и Runnable, пока они не будут обработаны. Да, есть что-то, что удерживает ваш Activity в памяти и не отпустит его, пока метод run этого Runnable не завершит выполнение. - person MaciejGórski; 31.12.2014
comment
@MaciejGórski, мой вопрос: если класс Foo (не Activity), мы создаем анонимный Runable, работающий в течение 10 часов, и Foo больше не упоминается, может ли экземпляр Foo быть GCed до выполнения выполняемой задачи? - person JaskeyLam; 01.01.2015
comment
@Jaskey Нет. Экземпляр Foo не будет подвергаться сборке мусора. Однако неразумно создавать Runnable с такой большой задержкой. Скорее всего, он никогда не будет выполнен, так как процесс вашего приложения, скорее всего, будет убит раньше. - person MaciejGórski; 01.01.2015

Да это утечка. Из-за того, как работают обработчики, они могут оставаться в живых в течение очень длительного периода времени и предотвращать сборку мусора для любых ресурсов, на которые они ссылаются. Вот хорошее объяснение: http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html

Если ваш runnable может пережить время жизни вашего объекта, вместо этого попробуйте подход в ответе здесь: https://stackoverflow.com/a/27825703/579234

Что вы делаете, так это добавляете еще один уровень объекта поверх обычного случая, который вызовет существующее предупреждение Lint о нестатических классах обработчиков, которое вызовет предупреждение Lint: Проверки Android:

HandlerLeak
-----------
Summary: Ensures that Handler classes do not hold on to a reference to an
outer class

Priority: 4 / 10
Severity: Warning
Category: Performance

In Android, Handler classes should be static or leaks might occur. Messages
enqueued on the application thread's MessageQueue also retain their target
Handler. If the Handler is an inner class, its outer class will be retained as
well. To avoid leaking the outer class, declare the Handler as a static nested
class with a WeakReference to its outer class.

Использование анонимного класса — это то же самое, что указано в http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9.5:

Анонимный класс всегда является внутренним классом (§8.1.3); он никогда не бывает статичным (§8.1.1, §8.5.1).

Итак, чтобы объяснить явно, ваш исполняемый файл содержит ссылку на «это», а обработчик содержит ссылку на исполняемый файл, поэтому «это» не будет собираться мусором, пока обработчик не будет мертв.

person Sogger    schedule 07.01.2015