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

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

Введение

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

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

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

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

Как предотвратить утечку памяти

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

Рекомендации для приложений Android

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

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

Ниже вы найдете подробное описание всех этих действий.

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

Если ваше приложение Android использует какой-либо ресурс, обязательно освободите его, когда он будет не нужен. Если вы этого не сделаете, ресурс продолжит использовать память. Дополнительную информацию вы можете найти в разделе Жизненный цикл объекта activity документации Android.

Удаление устаревших данных GoogleMap из GeoSDK

Кеширование библиотеки GoogleMap в NavigationView или MapView может приводить к утечке памяти. Библиотека GoogleMap передается напрямую из 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 SDK или Maps SDK, обязательно освободите ресурсы, перенаправив методы жизненного цикла (выделены жирным шрифтом) в navView. Это можно сделать с помощью класса NavigationView в Navigation SDK, а также с помощью класса MapView в Navigation SDK или Maps SDK. Вместо NavigationView и MapView можно соответственно использовать классы SupportNavigationFragment и SupportMapFragment. Эти фрагменты обрабатывают перенаправление методов жизненного цикла.

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. Убедитесь, что в ваших приложениях используются последние версии SDK.

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

Если вы выполнили все рекомендации, описанные ранее, но утечки памяти по-прежнему происходят, необходимо выполнить процесс отладки.

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

Для отладки выполните следующие действия:

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

Ниже вы найдете подробное описание этих шагов.

Шаг 1. Выполните действия, которые вызывают ошибку

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

  • Какие функции были активированы?

  • Что именно и в какой последовательности делал пользователь?

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

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

Попробуйте воспроизвести проблему в SDK последних версий. Возможно, она уже исправлена.

Шаг 2. Проверьте, действительно ли происходит утечка памяти

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

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

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

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

Если поведение приложения похоже на норму, вы можете устранить неполадку, настроив использование памяти в приложении.

Шаг 3. Выполните отладку на высоком уровне

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

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

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

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

Perfetto позволяет с точностью отслеживать несколько показателей и просматривать их на общей гистограмме. Подробнее…

Пользовательский интерфейс Perfetto

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

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

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

  • Procstats предоставляет важную агрегированную статистику за определенный период времени.

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

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

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

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

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

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

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

Подробные сведения приведены в разделах ниже.

Получите снимок дампа кучи

Получить снимок дампа кучи можно с помощью Android Debug Bridge (adb) или Профилировщика памяти Android Studio.

Снимок дампа кучи с помощью 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, следуйте этим инструкциям.

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

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

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

  2. Нажмите Run (Запустить), а затем выберите конфигурацию Debug (Отладка).

  3. Перейдите на вкладку Android Profiler (Профилировщик Android).

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

  5. Нажмите Open heap dump (Открыть дамп кучи) и выберите сгенерированный файл дампа. Профилировщик памяти покажет график с данными о том, как приложение использует память.

  6. Проанализируйте данные на графике:

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

    • Посмотрите, какие объекты требуют много памяти.

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

  7. Опираясь на это, найдите источник утечки и устраните ее.

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

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

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

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

Отладка памяти в нативном коде с помощью отслеживания распределения

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

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

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

LeakCanary – эффективный инструмент для поиска утечек памяти в приложениях Android. Подробнее…

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

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

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

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

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

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

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

    Примечание. Как правило, одних трассировок стека недостаточно, чтобы выполнить отладку утечки памяти. Вместе с ними необходимо прислать и другие виды данных.