Утечки памяти в Android и способы их локализации
На одном из проектов столкнулась с утечками памяти. Воспользовалась первым правилом разработчиков — загуглила. К моему глубокому разочарованию нашла много статей для программистов и совсем чуточку для тестировщиков. Большинство публикаций датированы 2011-2014 годом.
Ниже информация об утечках памяти, которая пригодится в 2017 году. Расскажу, чем их наличие грозит приложению, и перечислю несколько подходов к локализации.
Как утечки памяти могут отразиться на приложении
В приложении многие объекты имеют ограниченное время жизни. По его прошествию они уничтожаются сборщиком мусора. Но если объект доступен по цепочке ссылок, он не будет собран. Приложение выделяет память, но не освобождает её, это происходит, пока не исчерпается лимит и не случится краш.
Первый звоночек, а скорее зов горна, когда приложение крашится с ошибкой OutOfMemoryError. Именно эта ошибка говорит о том, что у приложения закончилась выделенная ему память.
Иногда все не так очевидно. У нас было стабильное и отлаженное приложение музыкального плеера. Было, пока на тестирование не поступил билд с парой новых экранов. Сначала приложение вело себя как обычно, но спустя минут десять начинало тормозить. Иногда зависало или закрывалось без алертов. Были краши в нативных «штуках», например GLRenderer.
Зачем тестировщику знать, как локализовывать утечки
Если сказать разработчику, что приложение само свернулось, тормозит или зависает, он скорее всего мало поможет. Пошлет вас локализовывать проблему и искать точные шаги воспроизведения. Разработчик надеется, что ваша инициатива померкнет и вы больше к нему с подобными глупостями приходить не станете. Но так как вы борец за качественную и стабильную работу приложения, просто так это не оставите. Надо локализовать проблему и обосновать разработчику её важность и критичность. Далее нужно действительно обдумать рациональность и необходимость «крестового похода».
Я делю все мобильные приложения на два типа:
Пользователь находится в приложении менее 10 минут
Примером могут послужить мобильные приложения для покупки билетов, мобильный банк, органайзер. Пользователь заходит в приложение для достижения конкретной цели.
В этом случае нет острой необходимости тщательно следить за утечками. Мелкие не успеют серьезно отразиться на работе приложения. А крупные, например, когда экран пересоздается при каждом заходе на него, легко найти.
Пользователь находится в приложении более 10 минут
Это история о музыкальных плеерах и соц. сетях, которые пользователь будет испытывать на прочность часами. Наравне с тестированием вы должны следить за утечками памяти и стабильной работой приложения. Даже небольшая утечка при сессии в 40 минут может отразиться на работе.
Если игра стоит свеч, действуйте. Расскажу про несколько подходов к локализации утечек памяти.
Подходы к локализации утечек
Локализовать голыми руками
Если повезет, сразу найдете последовательность, которая порождает утечки. Иногда приходится страдать. Локализовывать утечки вручную крайне трудоемко. Вам придется повторять однотипные действия десятки раз и смотреть, повлияло ли это на работу вашего приложения. Я оптимистично решила, что справлюсь голыми руками. Потратив неприлично большое количество времени, выявила закономерность. После 20 переключений между табами приложение работало некорректно. Затем и вовсе крашилось. На разных девайсах количество переключений варьировалось.
С этими знаниями пошла к разработчику. Спустя пару дней мне сказали, что утечку прикрыли и все будет летать. Внимание, спойлер. Так и было. До появления новых фич и экранов. Потом мы стали наблюдать знакомое неадекватное поведение.
Когда пытаетесь локализовать утечки только руками, нет гарантий, что утечка реально прикрыта. Возможно, она просто не воспроизводится по вашим шагам.
Совет: при локализации утечек в первую очередь проверьте вложенность экранов. Чем глубже, тем лучше. Надо учесть всё: пуши, объекты с картинками, анимации, списки, карты с отображающимися на них элементами, вертикальную и альбомную ориентацию.
Локализовать с помощью специальных инструментов
Мне помог Memory Monitor, стандартный инструмент из пакета андроид-студии. Подробнее можете ознакомиться на сайте Android Studio User Guide.
Если вы только приступили к поиску утечек, весьма полезно в совокупности с Memory Monitor запустить Monkey. Программа прекрасно подходит для стрессового тестирования. Она рандомно перемещается по приложению и генерирует пользовательские события: тап, клик, жест. Более подробно также на Android Studio User Guide.
Эти два инструмента помогут понять, есть ли у вас в приложении утечки. Пока обезьянка перемещается по приложению, вы наблюдаете за графиком на экране. Для работы приложения требуется определенный объем памяти, который постоянно задействован. Он должен держаться примерно на одном уровне. Перемещаясь по приложению обезьяна постепенно забивает память. После вызова сборщика мусора она должна вернуться к своему первоначальному значению. Если после сборщика память не очищается, значит, утечка.
Для поиска шагов воспроизведения обезьянка уже не подойдет. Нужно вручную и осознанно перемещаться по приложению, наблюдая за монитором.
Новый билд уже не решилась тестировать голыми руками. Запустила Memory Monitor, открыла приложение и наблюдала. Обнаружилась закономерность. Все треки, отображаемые на экране со списком композиций, повисали в памяти при переходе на следующий экран. Собрала дамп кучи и отправила разработчику. Оказывается, проблема в том, что метод RecyclerView onViewDetachedFromWindow для ячейки вызывался, когда ячейка трека пропадала с экрана после скролла, но не вызывался при закрытии экрана во время вызова onDestroyView. А именно там мы отписывались от изменения состояний ячеек треков. Поэтому и оставались ссылки на все списки после закрытия экрана.
Сейчас в приложении 22 экрана. Практически с каждого трека можно перейти на экран альбома. От релиза к релизу проблема с утечками только усугублялась, пока не вылилась в мое расследование. Только тогда мы действительно прикрыли утечки памяти.
Совет: при запуске Memory Monitor, остальные monitors ставьте на паузу, они могут помешать друг другу. Вызовите сборщик мусора перед тем, как сделать дамп кучи для разработчика.
Пишите в комментариях, как воюете с утечками.