Обзор управления памятью

Среда выполнения Android (ART) и виртуальная машина Dalvik используют подкачку и сопоставление памяти (mmapping) для управления памятью. Это означает, что любая память, которую модифицирует приложение — путем выделения новых объектов или касания отображаемых страниц — остается в оперативной памяти и не может быть выгружена. Единственный способ освободить память приложения — освободить ссылки на объекты, хранящиеся в приложении, сделав память доступной для сборщика мусора. Это за одним исключением: любые файлы, отображаемые без изменений, например код, могут быть выгружены из оперативной памяти, если система хочет использовать эту память где-то еще.

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

Сбор мусора

Среда управляемой памяти, такая как виртуальная машина ART или Dalvik, отслеживает каждое выделение памяти. Как только он определяет, что часть памяти больше не используется программой, он освобождает ее обратно в кучу без какого-либо вмешательства со стороны программиста. Механизм освобождения неиспользуемой памяти в среде управляемой памяти известен как сбор мусора . Сбор мусора преследует две цели: найти в программе объекты данных, к которым в будущем будет невозможно получить доступ; и вернуть ресурсы, используемые этими объектами.

Куча памяти Android является памятью поколений, что означает, что существуют разные сегменты распределения, которые он отслеживает, в зависимости от ожидаемого срока службы и размера выделяемого объекта. Например, недавно выделенные объекты относятся к Молодому поколению . Когда объект остается активным достаточно долго, его можно перевести в более старое поколение, а затем в постоянное поколение.

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

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

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

Более общую информацию о сборке мусора см. в разделе Сбор мусора .

Поделиться памятью

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

  • Каждый процесс приложения является ответвлением существующего процесса под названием Zygote. Процесс Zygote запускается, когда система загружается и загружает общий код и ресурсы платформы (например, темы действий). Чтобы запустить новый процесс приложения, система разветвляет процесс Zygote, затем загружает и запускает код приложения в новом процессе. Этот подход позволяет использовать большую часть страниц оперативной памяти, выделенных для кода и ресурсов платформы, для всех процессов приложения.
  • Большая часть статических данных отображается в процессе. Этот метод позволяет совместно использовать данные между процессами, а также позволяет выгружать их при необходимости. Примеры статических данных включают в себя: код Dalvik (путем его размещения в предварительно связанном файле .odex для прямого сопоставления), ресурсы приложения (путем создания таблицы ресурсов как структуры, которую можно сопоставить, и выравнивания zip-записей APK). и традиционные элементы проекта, такие как собственный код в файлах .so .
  • Во многих местах Android использует одну и ту же динамическую оперативную память для всех процессов, используя явно выделенные области общей памяти (с помощью ashmem или gralloc). Например, поверхности окон используют общую память между приложением и компоновщиком экрана, а буферы курсора используют общую память между поставщиком контента и клиентом.

Из-за интенсивного использования общей памяти определение объема памяти, используемого вашим приложением, требует осторожности. Методы правильного определения использования памяти вашим приложением обсуждаются в разделе «Исследование использования оперативной памяти» .

Выделение и освобождение памяти приложения

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

Логический размер кучи не совпадает с объемом физической памяти, используемой кучей. При проверке кучи вашего приложения Android вычисляет значение, называемое пропорциональным заданным размером (PSS), которое учитывает как грязные, так и чистые страницы, которые используются совместно с другими процессами, но только в количестве, пропорциональном тому, сколько приложений используют эту оперативную память. Это общее количество (PSS) — это то, что система считает объемом вашей физической памяти. Дополнительную информацию о PSS см. в руководстве «Исследование использования оперативной памяти» .

Куча Dalvik не сжимает логический размер кучи, а это означает, что Android не дефрагментирует кучу, чтобы закрыть пространство. Android может уменьшать логический размер кучи только в том случае, если в конце кучи есть неиспользуемое пространство. Однако система все равно может уменьшить физическую память, используемую кучей. После сборки мусора Dalvik просматривает кучу и находит неиспользуемые страницы, а затем возвращает эти страницы ядру с помощью madvise. Таким образом, парное выделение и освобождение больших фрагментов должно привести к освобождению всей (или почти всей) используемой физической памяти. Однако освобождение памяти из небольших выделений может быть гораздо менее эффективным, поскольку страница, используемая для небольшого выделения, все еще может использоваться совместно с чем-то еще, что еще не освобождено.

Ограничить память приложения

Чтобы поддерживать функциональную многозадачную среду, Android устанавливает жесткое ограничение на размер кучи для каждого приложения. Точный предел размера кучи зависит от устройства в зависимости от общего объема оперативной памяти, доступной устройству. Если ваше приложение достигло емкости кучи и пытается выделить больше памяти, оно может получить OutOfMemoryError .

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

Переключение приложений

Когда пользователи переключаются между приложениями, Android сохраняет в кеше приложения, которые не являются приоритетными, то есть невидимыми пользователю или не запускающими службы приоритетного плана, такие как воспроизведение музыки. Например, когда пользователь впервые запускает приложение, для него создается процесс; но когда пользователь покидает приложение, этот процесс не завершается. Система сохраняет процесс в кэше. Если позже пользователь возвращается в приложение, система повторно использует процесс, тем самым ускоряя переключение приложения.

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

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

Дополнительную информацию о том, как процессы кэшируются, когда они не выполняются на переднем плане, и как Android решает, какие из них можно завершить, см. в руководстве «Процессы и потоки» .

Стресс-тест памяти

Хотя проблемы с нагрузкой на память менее распространены на устройствах более высокого класса, они все равно могут вызывать проблемы у пользователей устройств с низким объемом оперативной памяти, например, на устройствах под управлением Android (версия Go). Важно попытаться воспроизвести эту среду с нехваткой памяти, чтобы вы могли написать инструментальные тесты для проверки поведения приложения и улучшения работы ваших пользователей на устройствах с низким объемом памяти.

Стрессовый тест приложения

Стрессовый тест приложения ( stressapptest ) — это тест интерфейса памяти, который помогает создавать реалистичные ситуации с высокой нагрузкой для проверки различных ограничений памяти и оборудования для вашего приложения. Благодаря возможности определять ограничения по времени и памяти это позволяет вам писать инструменты для проверки реальных ситуаций с большим объемом памяти. Например, используйте следующий набор команд, чтобы поместить статическую библиотеку в файловую систему данных, сделать ее исполняемой и запустить стресс-тест в течение 20 секунд с объемом 990 МБ:
    adb push stressapptest /data/local/tmp/
    adb shell chmod 777 /data/local/tmp/stressapptest
    adb shell /data/local/tmp/stressapptest -s 20 -M 990

  

Дополнительную информацию об установке инструмента, общих аргументах и ​​информации об обработке ошибок см. в документации stressapptest .

Наблюдения за стрессаптестом

Такие инструменты, как stressapptest можно использовать для запроса выделения памяти, превышающей свободно доступную. Этот тип запроса может вызывать различные предупреждения, о которых вам следует знать со стороны разработчиков. Три основных предупреждения, которые могут возникнуть из-за нехватки памяти, включают в себя:
  • SIGABRT: Это фатальный собственный сбой вашего процесса из-за запроса на выделение памяти размером больше, чем свободная память, в то время как система уже испытывает нехватку памяти.
  • SIGQUIT : создает дамп основной памяти и завершает процесс, если он обнаружен вашим инструментальным тестом.
  • TRIM_MEMORY_EVENTS : эти обратные вызовы доступны на Android 4.1 (уровень API 16) и выше и предоставляют подробные оповещения об использовании памяти для вашего процесса.