Неправильный AnimationListener
Для привлечения внимания к новому функционалу было принято решение воспользоваться анимацией. Необходимо было показать на короткий период View с подсказкой. Пример можно увидеть на изображении ниже:
Позже обнаружилась утечка Activity.
Проблема заключалась в том, что анимация fadeOut использовала слушателя от анимации fadeIn, что приводило к бесконечному запуску анимации fadeOut.
Исправить это было достаточно легко.
Анимации часто являются причинами утечек. Этого очень легко достигнуть забыв остановить бесконечную анимацию или запустив анимацию на неоправданно длительное время.
android.widget.Editor$Blink
Бывает утечки памяти находятся и в самой платформе. Однажды я получил такой отчет:
Чтобы воспроизвести утечку достаточно создать Dialog с EditText внутри и что-нибудь в нем напечатать.
Как оказалось, это известный баг и он уже исправлен. Там же можно найти хак, для исправления этой утечки:
Утечка от производителя. Singleton — зло
В одном из отчетов я получил утечку с не публичным, неизвестным мне ранее классом. Оказалось, что это приватный класс от одного популярного производителя.
На этот раз мне опять повезло и я нашел готовое решение в сети (1, 2).
Результаты
В результате количество OutOfMemoryError уменьшилось почти в 7 раз. Так же уменьшилось количество RuntimeException,InflateException: часть из них — это пойманные и обернутые OutOfMemoryError. Одним из ограничивающих факторов в исправлении такого рода ошибок является то, что я не могу делать обновления, которые не содержат внешних изменений (для пользователей выглядит как “Пустое обновление”) В таких случаях приходится ждать реализации нового функционала для проверки результатов.
Выводы
- Документируйте не тривиальные/не очевидные моменты в коде, которые исправляют утечки памяти. Часто они выглядят подозрительно.
- Не ошибается тот, кто ничего не делает. Иногда приходится фиксить утечки памяти в чужом коде.
- Нужно знать свой инструментарий (hprof-conv, VisualVM, OQL, leakcanary)
- Обязательно делайте измерения и следите за ними(crash-report tools).
- Не допускайте увеличения количества ошибок.
Ссылки
- VisualVM visualvm.java.net
- OQL visualvm.java.net/oqlhelp.html
- leakcanary github.com/square/leakcanary
- Исходный код примеров github.com/kurganec/leak_examples
Комментарии (10)
Nucleotide
10.12.2015 06:30Позже обнаружилась утечка Activity.
Да, но как только Вью задестроится, задестроится и анимация, и утечки не будет. Разве нет?
Вопрос в том, действительно останавливать бесконечную анимацию вручную, ведь она со всеми вью задестроится и всё, никаких проблем?thelongrunsmoke
10.12.2015 08:30Это может привести к OutOfMemoryError до уничтожения View. Особенно при сложном интерфейсе или ошибках разработки. Например, ViewPager со сложным фрагментом, который почему-то не выгружают из памяти.
korniltsev
10.12.2015 16:02+1Да, но как только Вью задестроится, задестроится и анимация, и утечки не будет. Разве нет?
Нет, это не так.
Необходимо понимать как работают Animator и Choreographer. До тех пор, пока анимация не завершится, аниматор будет добавлять колбэк(AnimationHandler) в одну из очередей Choreographer. Этот колбэк исполняется на каждый фрейм и меняет значение пропертей(альфа, смещение и т.д.). Получается бесконечная/долгая анимация будет бесконечно/долго отправдять такие сообщение в очередь на исполнение.
Получается следующая цепочка, которая не дает gc собрать вьюшку(а с ней и контекст):
ThreadLocal(Choreographer) -> у Choreographer жесткая ссылка на очередь колбэков -> у колбэков жесткая ссылка на аниматор -> у аниматора жесткая ссылка на вьюху
DZVang
10.12.2015 10:59+1А с чем связано использование VisualVM, а не MAT? Есть какие-то плюсы, минусы, подводные камни. Во всех статьях, с которыми я сталкивался всегда MAT использовался.
korniltsev
10.12.2015 15:16jvisualvm поставляется вместе с jdk. Еще в jvisualvm есть удобные сниппеты для OQL.
скринMikhail_dev
11.12.2015 00:17О чем статья? О том что в анимации могут быть утечки? Вот и назовите тогда так статью, хотя на статью это не тянет.
Причем тут singleton, при этом еще и зло? Недавно только дебаты устраивали на счет этого, и пришли к выводу что зло он только потому, что с ним сложно (или может невозможно) использовать автоматические тесты. Так что не особо понятно как к статье это относится, что он зло, и что он синглтон.
В итоге ничего толком не написано. Сухие ссылки на инструменты, и не более того. Последний вывод вообще убил.
Не допускайте увеличения количества ошибок.
Серьёзно? Ну ок, а я то думал.
P.S. и если уж на то пошло, то надо было и про WeakReference словечко замолвить, ну или как вы сделали, ссылку дать.
TARAKAN
Я понимаю, что из-за небольших ошибок не выкладывать, но если исправлено их несколько или какая-то массовая ошибка с тем же OutOfMemory, то почему нельзя выложить? Многие приложения выкладываются с одним единственным пунктом «Bugs fixed» бывает еще описывается какие баги, но зачастую только общий текст, что исправлены ошибки и улучшена стабильность приложения.
korniltsev
В собственных проектах я не стесняюсь делать обновления из-за подобного рода ошибок.
Однако на текущем месте работы не принятно делать пустых обновлений. У нас пустые обновления негативно влияют на рейтинг в маркете. Рейтинг может влиять на выдачу в поиске.
Bringoff
У всех, конечно, свои странности, но… А падения от OOM на рейтинг полжительно влияют, что ли?
TARAKAN
Не в курсе всей логики пользователей, может у вас там какое-то «исследование» проводили, но я по наблюдению отзывов заметил, что люди быстрее бегут ставить рейтинг после нескольких падений и понятно, что рейтинг зачастую ставят сразу 1, а вот после обновления часто забывают обновить рейтинг.
Вот смотрю сейчас Instagram приложение, у них в «Что нового» уже давным давно один и тот же текст, а приложение помню обновлялось раз 5 точно, аналогично с другими, там может быть 10 обновлений без новых фич.