Site icon AppTractor

Фризы и ANR? Проверьте утечки памяти!

В этой статье я покажу, что утечки памяти в Android приводят к замедлениям, зависаниям и ANR чаще, чем к OutOfMemoryError сбоям.

Задержка при навигации

В Square мы отслеживаем метрики производительности, ориентированные на пользователей — в частности, задержку взаимодействия. Мы отслеживаем этот показатель для каждой навигации в приложении (пример реализации: «Время реакции на тап: Jetpack Navigation«).

Другими словами, для каждой навигации мы сообщаем метрику задержки, которая измеряет время от момента получения касания до момента обновления экрана, т.е. насколько велика задержка, воспринимаемая пользователями.

val durationMillis = frameCommitted - actionUpMotionEvent.eventTime
analytics.logNavigation(
  originScreen,
  destinationScreen,
  durationMillis
)

Использование памяти при навигации

Такие показатели потребления ресурсов, как использование памяти, часто представляются в виде временных рядов, что не очень удобно при попытке соотнести использование приложений с утечками памяти.

В январе 2023 года Павел Ставицкий опубликовал в блоге Lyft Engineering статью “Обнаружение утечек памяти в Android в продакшене”.

Одна из интересных идей статьи заключается в том, чтобы сообщать метрики использования памяти при каждой навигации по экрану, а не в виде временного ряда, поскольку утечки памяти имеют тенденцию накапливаться по мере использования приложения.

Давайте обновим нашу аналитику навигации, чтобы добавить данные об использовании памяти:

val runtime = Runtime.getRuntime()
val javaHeapUsage = runtime.totalMemory() - runtime.freeMemory()

analytics.logNavigation(
  sourceScreen,
  destinationScreen,
  durationMillis,
  javaHeapUsage
)

Ограничение памяти

Если бы Android-устройства обладали бесконечной памятью, утечки памяти не были бы проблемой. Но на устройствах с Android оперативная память ограничена, каждому приложению разрешено использовать только часть оперативной памяти устройства для своей Java-кучи, и утечки памяти становятся проблемой, когда использование памяти приближается к пределу. Этот предел настраивается для каждого устройства и может быть запрошен с помощью Runtime.maxMemory():

val javaHeapLimit = Runtime.getRuntime().maxMemory()

analytics.logNavigation(
  sourceScreen,
  destinationScreen,
  durationMillis,
  javaHeapUsage,
  javaHeapLimit
)

Пример протекающей сессии

Мы можем построить график использования памяти с течением времени для одного сессии, где каждая точка данных — это одна навигация. Вот реальный пример сессии с 1591 переходом, где мы видим, что использование памяти растет с течением времени:

Обратите внимание, что использование кучи Java постоянно скачет вверх и вниз по мере работы GC, но тенденция к росту  указывает на утечку памяти. Применение линейной регрессии показало, что наклон составляет +146 КБ на навигацию.

Использование памяти и задержка при навигации

Добавим к графику задержку навигации:

Обратите внимание, что задержка при навигации остается довольно ровной на протяжении всей сессии, пока использование памяти не приблизится к пределу, и тогда задержка при навигации резко возрастает. Мы можем увеличить масштаб последних 200 переходов:

В этом примере пользовательский интерфейс замирает на несколько секунд, пока GC блокирует основной поток для освобождения памяти. Это начинает происходить, когда память приближается к пределу в 18 МБ.

Прогрессирующее влияние утечек памяти

По мере приближения памяти кучи Java к лимиту памяти приложения, влияние утечек памяти становится все более заметным.

  1. Сначала небольшие GC-паузы приводят к замиранию анимации.
  2. Затем более длительные GC-паузы приводят ко все более длительному зависанию пользовательского интерфейса, причем на несколько секунд.
  3. Если основной поток замирает более чем на 5 секунд в ожидании диспетчеризации тач событий, приложение выдает ошибку Application Not Responding (ANR).
  4. В конце концов, памяти остается так мало, что мы не можем выделять новые объекты, и приложение завершается с исключением OutOfMemoryError.

Игнорирование реального влияния утечек памяти

Если у вас есть отчетность о сбоях и процесс исправления самых распространенных ошибок, то вы молодцы! К сожалению, по двум причинам нельзя просто смотреть на ошибки OutOfMemoryError, чтобы решить, что следует заняться устранением утечек памяти:

Выводы

Источник

Exit mobile version