Рекомендации по управлению памятью

В этом документе предполагается, что вы следовали рекомендациям по управлению памятью для приложений Android, например, разделу Управление памятью вашего приложения .

Введение

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

В этом документе представлены рекомендации по предотвращению, обнаружению и устранению утечек памяти в вашем коде. Если вы опробовали методы, описанные в этом документе, и подозреваете утечку памяти в наших SDK, см. статью «Как сообщить о проблемах с Google SDK» .

Прежде чем обратиться в службу поддержки

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

Предотвращение утечек памяти

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

Лучшие практики для приложений Android

Проверьте, выполнили ли вы все следующие действия в своем приложении для Android:

  1. Освободите неиспользуемые ресурсы .
  2. Отмените регистрацию слушателей, когда они больше не нужны .
  3. Отменяйте задачи, когда они не нужны .
  4. Методы жизненного цикла вперед для высвобождения ресурсов .
  5. Используйте последние версии SDK

Подробную информацию о каждом из этих методов см. в следующих разделах.

Освободить неиспользуемые ресурсы

Когда ваше приложение Android использует ресурс, обязательно освобождайте его, когда он больше не нужен. В противном случае ресурс продолжит занимать память даже после того, как приложение завершит работу с ним. Подробнее см. в разделе «Жизненный цикл активности» в документации Android.

Освобождение устаревших ссылок GoogleMap в GeoSDK

Распространённая ошибка заключается в том, что GoogleMap может вызывать утечку памяти при кэшировании с помощью NavigationView или MapView. GoogleMap имеет отношение 1 к 1 с NavigationView или MapView, из которых он извлекается. Необходимо либо убедиться, что GoogleMap не кэшируется, либо ссылка на него освобождается при вызове NavigationView#onDestroy или MapView#onDestroy. При использовании NavigationSupportFragment, MapSupportFragment или вашего собственного фрагмента, обёртывающего эти представления, ссылка должна быть освобождена в Fragment#onDestroyView.

class NavFragment : SupportNavigationFragment() {

  var googleMap: GoogleMap?

  override fun onCreateView(
    inflater: LayoutInflater,
    parent: ViewGroup?,
    savedInstanceState: Bundle?,
  ): View  {
    super.onCreateView(inflater,parent,savedInstanceState)
    getMapAsync{map -> googleMap = map}
  }

  override fun onDestroyView() {
    googleMap = null
  }
}

Отмените регистрацию слушателей, когда они больше не нужны

Когда ваше Android-приложение регистрирует обработчик событий, например, нажатие кнопки или изменение состояния представления, обязательно отмените регистрацию обработчика, когда приложению больше не нужно отслеживать это событие. В противном случае обработчики продолжат занимать память даже после того, как приложение завершит работу с ними.

Например, предположим, что ваше приложение использует Navigation SDK и вызывает следующий прослушиватель для прослушивания событий прибытия: метод addArrivalListener для прослушивания событий прибытия; он также должен вызывать removeArrivalListener , когда ему больше не нужно отслеживать события прибытия.

var arrivalListener: Navigator.ArrivalListener? = null

fun registerNavigationListeners() {
  arrivalListener =
    Navigator.ArrivalListener {
      ...
    }
  navigator.addArrivalListener(arrivalListener)
}

override fun onDestroy() {
  navView.onDestroy()
  if (arrivalListener != null) {
    navigator.removeArrivalListener(arrivalListener)
  }

  ...
  super.onDestroy()
}

Отменяйте задачи, когда они не нужны

Когда приложение Android запускает асинхронную задачу, например, загрузку или сетевой запрос, обязательно отмените её после завершения. Если задача не отменена, она продолжит выполняться в фоновом режиме даже после того, как приложение завершит её выполнение.

Более подробную информацию о передовых практиках см. в разделе Управление памятью приложения в документации Android.

Методы жизненного цикла вперед для высвобождения ресурсов

Если ваше приложение использует Navigation или Maps SDK, обязательно освободите ресурсы, перенаправив методы жизненного цикла (выделены жирным шрифтом) в navView . Это можно сделать с помощью NavigationView в Navigation SDK или MapView в Maps или Navigation SDK. Вы также можете использовать SupportNavigationFragment или SupportMapFragment вместо прямого использования NavigationView и MapView соответственно. Фрагменты поддержки обеспечивают перенаправку методов жизненного цикла.

class NavViewActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    navView = ...
    navView.onCreate(savedInstanceState)
    ...
  }

  override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)
    navView.onSaveInstanceState(savedInstanceState)
  }

  override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)
    navView.onTrimMemory(level)
  }

  /* Same with
    override fun onStart()
    override fun onResume()
    override fun onPause()
    override fun onConfigurationChanged(...)
    override fun onStop()
    override fun onDestroy()
  */
}

Используйте последние версии SDK

Пакеты SDK Google постоянно обновляются, добавляя новые функции, исправления ошибок и улучшения производительности. Поддерживайте SDK в вашем приложении в актуальном состоянии, чтобы получать эти исправления.

Отладка утечек памяти

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

Прежде чем начать , вам следует ознакомиться с тем, как Android управляет памятью. Подробнее см. в статье «Обзор управления памятью в Android».

Чтобы устранить утечки памяти, выполните следующие действия:

  1. Воссоздайте проблему . Этот шаг необходим для её устранения.
  2. Проверьте, соответствует ли потребление памяти ожидаемому . Убедитесь, что повышенное потребление, которое кажется утечкой, на самом деле не соответствует объёму памяти, необходимому для работы вашего приложения.
  3. Отладка на высоком уровне . Существует несколько утилит для отладки. Три стандартных набора инструментов помогают отладить проблемы с памятью в Android: Android Studio, Perfetto и утилиты командной строки Android Debug Bridge (adb).
  4. Проверьте использование памяти вашим приложением . Получите дамп кучи и отслеживайте распределение памяти, а затем проанализируйте его.
  5. Устранить утечки памяти .

В следующих разделах эти шаги рассматриваются подробно.

Шаг 1: Воссоздайте проблему

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

  • Какой набор функций активирован?

  • Какая конкретная последовательность действий пользователя приводит к утечке?

    • Пробовали ли вы несколько раз активировать эту последовательность?
  • Какие состояния жизненного цикла прошло приложение?

    • Пробовали ли вы несколько итераций через различные состояния жизненного цикла?

Убедитесь, что вы можете воспроизвести проблему в последней версии SDK. Возможно, проблема в предыдущей версии уже была исправлена.

Шаг 2: Проверьте, соответствует ли использование памяти приложением ожидаемому.

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

  • Вероятная утечка: активация сценария посредством нескольких итераций со временем приводит к увеличению использования памяти.

  • Вероятное ожидаемое использование памяти : память восстанавливается после остановки сценария.

  • Возможное ожидаемое использование памяти : использование памяти увеличивается в течение некоторого времени, а затем снижается. Это может быть связано с ограниченным кэшем или другим ожидаемым использованием памяти.

Если поведение приложения, вероятно, связано с ожидаемым потреблением памяти, проблему можно решить, управляя памятью приложения. Подробнее см. в разделе Управление памятью приложения .

Шаг 3: Отладка на высоком уровне

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

Профилировщик памяти Android Studio

Этот инструмент отображает визуальную гистограмму потребляемой памяти. С помощью этого же интерфейса можно также запускать дампы кучи и отслеживать распределение памяти. Этот инструмент рекомендуется использовать по умолчанию. Подробнее см. в статье «Профилировщик памяти Android Studio» .

Счетчики памяти Perfetto

Perfetto обеспечивает точный контроль над отслеживанием нескольких показателей и представляет все данные в виде единой гистограммы. Подробнее см. в разделе «Счётчики памяти Perfetto» .

Perfetto user interface

Утилиты командной строки Android Debug Bridge (ADB)

Многое из того, что можно отслеживать с помощью Perfetto, также доступно в виде утилиты командной строки adb , к которой можно обращаться напрямую. Вот несколько важных примеров:

  • Meminfo позволяет просматривать подробную информацию о памяти на определенный момент времени.

  • Procstats предоставляет некоторые важные обобщенные статистические данные за определенный период времени.

Важнейшая статистика, на которую следует обратить внимание, — это максимальный объём физической памяти (maxRSS), потребляемый приложением с течением времени. MaxPSS может быть неточным. Чтобы повысить точность, воспользуйтесь флагом adb shell dumpsys procstats --help –start-testing .

Отслеживание распределения

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

Шаг 4: Проверьте использование памяти вашим приложением с помощью дампа кучи

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

Android Studio может обнаруживать утечки памяти, которые невозможно исправить сборщиком мусора. При создании дампа кучи Android Studio проверяет наличие активности или фрагмента, которые всё ещё доступны, но уже уничтожены.

  1. Сохранение дампа кучи .
  2. Проанализируйте дамп кучи, чтобы найти утечки памяти .
  3. Устранить утечки памяти .

Подробности смотрите в следующих разделах.

Захват кучи дампа

Для захвата дампа кучи можно использовать Android Debug Bridge (adb) или Android Studio Memory Profiler.

Используйте adb для создания дампа кучи

Чтобы создать дамп кучи с помощью adb , выполните следующие действия:

  1. Подключите Android-устройство к компьютеру.
  2. Откройте командную строку и перейдите в каталог, где находятся инструменты adb.
  3. Чтобы создать дамп кучи, выполните следующую команду:

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. Чтобы получить дамп кучи, выполните следующую команду:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

Используйте Android Studio для создания дампа кучи

Чтобы захватить дамп кучи с помощью Android Studio Memory Profiler, выполните следующие действия, описанные в разделе Захват дампа кучи в Android.

Проанализируйте дамп кучи, чтобы найти утечки памяти.

После создания дампа кучи вы можете проанализировать его с помощью инструмента Android Studio Memory Profiler. Для этого выполните следующие действия:

  1. Откройте свой Android-проект в Android Studio.

  2. Выберите «Выполнить» , а затем выберите конфигурацию «Отладка» .

  3. Откройте вкладку «Профилировщик Android» .

  4. Выберите Память .

  5. Выберите «Открыть дамп кучи» и выберите созданный вами файл дампа кучи. Профилировщик памяти отобразит график использования памяти вашим приложением.

  6. Используйте график для анализа дампа кучи:

    • Определите объекты, которые больше не используются.

    • Определите объекты, использующие большой объем памяти.

    • Посмотрите, сколько памяти использует каждый объект.

  7. Используйте эту информацию, чтобы сузить поиск или найти источник утечки памяти и устранить его.

Шаг 5: Устранение утечек памяти

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

Другие инструменты отладки

Если после выполнения этих шагов вы все еще не нашли и не устранили утечку памяти, попробуйте следующие инструменты:

Отладка памяти в машинном коде с отслеживанием выделения памяти

Даже если вы не используете нативный код напрямую, некоторые распространённые библиотеки Android, включая Google SDK, всё же используют его. Если вы считаете, что утечка памяти происходит в нативном коде, существует несколько инструментов для её отладки. Отслеживание распределения памяти с помощью Android Studio или heapprofd (также совместимого с Perfetto) — отличный способ выявить потенциальные причины утечки памяти и зачастую самый быстрый способ отладки.

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

Выявляйте утечки с помощью LeakCanary

LeakCanary — мощный инструмент для выявления утечек памяти в приложениях Android. Чтобы узнать больше об использовании LeakCanary в вашем приложении, посетите сайт LeakCanary .

Как сообщить о проблемах с Google SDK

Если вы попробовали методы, описанные в этом документе, и подозреваете утечку памяти в наших SDK, обратитесь в службу поддержки клиентов, предоставив как можно больше следующей информации:

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

  • Дампы кучи, полученные из вашего приложения, в котором проблема была воссоздана . Сохраните дампы кучи в двух разных моментах времени, которые показывают, что использование памяти значительно увеличилось.

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

  • Отчет об ошибке, созданный после того, как вы воссоздали условие утечки.

  • Трассировки стека любых сбоев, связанных с памятью .

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