Инструменты для быстрой оценки

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

dumpsys meminfo

Наиболее распространенный способ получить общее представление об использовании памяти приложением — это dumpsys meminfo . Для получения подробной информации, включая все сопоставления памяти, часто полезно добавить флаг -a :

adb shell dumpsys meminfo -a <package_name_or_pid>

Вот пример выходных данных приложения, испытывающего нехватку памяти (обратите внимание на высокое значение SwapPss):

** MEMINFO in pid 4562 [com.android.memorylab] **
                   Pss  Private  Private  SwapPss      Rss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty    Total     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------   ------
  Native Heap      394      268       64    17440     4264    29416    18128     8040
  Dalvik Heap      165        8        0      672     8196    40949    32757     8192
 Dalvik Other       47       28        0      604     1132
        Stack      112      108        4      312      120
...
        TOTAL    33197      580      788    21920   167776    70365    50885    16232

 App Summary
                       Pss(KB)                        Rss(KB)
                        ------                         ------
           Java Heap:       12                          32608
         Native Heap:      268                           4264
                Code:      724                         125672
               Stack:      108                            120
            Graphics:        0                              0
       Private Other:      256
              System:    31829
             Unknown:                                    5112

           TOTAL PSS:    33197            TOTAL RSS:   167776       TOTAL SWAP PSS:    21920

 Objects
               Views:       15         ViewRootImpl:        1
         AppContexts:        5           Activities:        1
...

Интерпретация данных

  • Pss Total : Пропорциональный размер набора. Это сумма Private Dirty
    • Private Clean + доля памяти, используемая вашим процессом совместно с другими процессами (например, образом загрузки Zygote). Это лучший показатель того, "за какой объем памяти отвечает это приложение".
  • Private Dirty : Это оперативная память, используемая только вашим процессом и подвергнутая модификациям (или анонимная память). Это наиболее важный показатель для обнаружения утечек, поскольку эту память невозможно удалить.
  • Private Clean : Память, используемая только вашим процессом, но представляющая собой неизмененную копию файла на хранилище (например, DEX-файла). Она может быть сброшена операционной системой при нехватке памяти.
  • SwapPss : Память, которая была сжата и выгружена в ZRAM. Это может произойти либо из-за нехватки памяти в системе, либо потому, что среда выполнения Android определила фоновые процессы для сжатия (перемещения их неиспользуемых или неактивных страниц в ZRAM). В примере dumpsys почти вся Native Heap и Dalvik Heap были перемещены в ZRAM.
  • Краткое описание приложения : Категоризация более высокого уровня, объединяющая подробные строки в понятные категории.
  • Объекты : Полезно для отслеживания утечек экземпляров Activities и ViewRootImpl . Если вы выйдете из приложения и принудительно выполните сборку мусора, счетчик Activities должен вернуться к нулю.

Практическое упражнение: быстрая сортировка запросов с помощью meminfo.

В этом упражнении показано, как различные типы выделения памяти отображаются в meminfo .

  1. Запустите приложение :

    adb shell am start -W -n com.android.memorylab/.MainActivity
    
  2. Проверьте базовый уровень памяти :

    adb shell dumpsys meminfo -s com.android.memorylab
    

    (Флаг -s предоставляет краткое и лаконичное описание категорий памяти).

  3. Выделить память Java : Нажмите кнопку «Выделить память Java (10 МБ)» 3 раза в приложении.

  4. Обратите внимание на изменение : снова запустите meminfo -s . Вы должны увидеть, что значение Java Heap увеличилось примерно на 30 000 КБ (30 МБ).

  5. Выделить собственную память : Нажмите один раз кнопку « Выделить собственную память (1 ГБ несжимаемой памяти)» .

  6. Обратите внимание на изменение : снова запустите meminfo -s . Значение Native Heap теперь должно значительно увеличиться (~1 000 000 КБ).

Глобальный обзор

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

adb shell dumpsys meminfo

Здесь представлена ​​сводка об использовании оперативной памяти, включая общий объем оперативной памяти, свободную оперативную память и список процессов, отсортированных по PSS.

Ключевые системные показатели

  • ZRAM : Сжатый файл подкачки в оперативной памяти.
  • DMA-BUF : Графические и разделяемые буферы памяти. Современные версии Android активно используют DMA-BUF для отрисовки пользовательского интерфейса. Если общий объем DMA-BUF очень велик, проверьте процессы, интенсивно использующие графику, с помощью /sys/kernel/debug/dma_buf/bufinfo .
  • Потерянная оперативная память : память, которая не учитывается ядром или каким-либо процессом (например, аппаратно зарезервированные области).

показать карту

Для получения более подробной информации о расположении объектов в памяти процесса используйте showmap . Этот инструмент считывает данные из файла /proc/<pid>/smaps .

adb shell showmap <pid>

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

  • [anon:scudo:primary] или [anon:libc_malloc] : собственный объем памяти кучи. В современных версиях Android используется распределитель памяти Scudo.
  • *.art : Образы кучи Java (например, образ загрузки).
  • [anon:dalvik-main space] : Основная куча Java.
  • *.so : Разделяемые библиотеки.
  • *.{oat,dex,odex,vdex} : Скомпилированный DEX-код.

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

# Sort by private clean + private dirty memory
adb shell showmap <pid> | awk '{print $6+$7, $0}' | sort -rn | head -n 10

Пример выходных данных:

 virtual                     shared   shared  private  private                   Anon      Shmem     File      Shared   Private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS HugePages PmdMapped PmdMapped Hugetlb  Hugetlb    Locked    # object
-------- -------- -------- -------- -------- -------- -------- -------- -------- --------- --------- --------- -------- -------- -------- ---- ------------------------------
2776    18176     5784     2823        0     3008      308     2468     4700     4700         0         0         0        0        0        0   47 [anon:scudo:primary]
560      952      876      657      316        0      560        0       12       12         0         0         0        0        0        0    8 /data/app/.../base.apk
448   393216      448      448        0        0        0      448        0        0         0         0         0        0        0        0    2 [anon:dalvik-main space]
...

Практическое упражнение: углубленное изучение с помощью Showmap.

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

  1. Mmap Clean Memory : Нажмите один раз кнопку Mmap Clean Anon Memory (10MB) в приложении MemoryLab.
  2. Получение идентификатора процесса (PID) : пользовательский интерфейс приложения отображает его PID. Вы также можете найти его с помощью этой команды:

    adb shell pidof com.android.memorylab
    
  3. Проверить с помощью команды showmap :

    # Replace <PID> with the actual process ID from the previous step
    adb shell showmap <PID> | grep "memorylab-clean"
    
  4. Определите отображение : вы должны увидеть анонимное отображение с меткой [anon:memorylab-clean] и виртуальным размером ровно 10240 КБ. Обратите внимание, что значение параметра «dirty» для этого отображения, скорее всего, равно 0. Это «чистая» анонимная память, потому что мы использовали mmap для резервирования виртуального пространства, но еще не записывали в него данные (поэтому физическая оперативная память фактически не используется).

  5. Сравнение с нативной кучей : сравните это с отображениями [anon:scudo:primary] , которые появились бы в showmap , если бы вы запустили его после шага 5 упражнения meminfo . Поскольку этот нативный объем в 1 ГБ фактически записывает данные на каждую страницу, эти страницы являются «грязными» и будут часто отображаться в столбце «грязные частные ».

  6. Полезный совет (для больших объектов) : Обратите внимание на выделение памяти в куче Java из упражнения meminfo . Поскольку это массивы размером 10 МБ, они, вероятно, отображаются в showmap как [anon:dalvik-free list large object space] . Среда выполнения Android (ART) помещает объекты размером более 12 КБ в отдельное пространство больших объектов (LOS), чтобы избежать фрагментации в основной куче.

procstats

В то время как meminfo предоставляет моментальный снимок состояния, procstats предоставляет исторические данные, периодически отслеживая состояние и использование памяти всех запущенных приложений. Это основной инструмент, используемый для долгосрочного мониторинга состояния памяти, и именно он отвечает за отображение отчетов об использовании памяти в настройках Android.

# View stats for the last 3 hours
adb shell dumpsys procstats --hours 3

Понимание штатов

Вывод procstats может быть очень объёмным. Наиболее важная часть — это метрики в скобках рядом с состоянием каждого процесса:

Top: 100% (54MB-64MB-67MB / 49MB-58MB-61MB / 110MB-120MB-130MB over 4)

Формат: (% времени в состоянии) (minPss-avgPss-maxPss / minUss-avgUss-maxUss / minRss-avgRss-maxRss по сэмплам)

  • PSS (Proportional Set Size) : Общая память распределяется между процессами пропорционально.
  • Uss (Unique Set Size) : Память, используемая только этим процессом (эквивалентно Private Clean + Private Dirty).
  • RSS (Resident Set Size) : Общий объем используемой физической памяти, включая все разделяемые страницы (имеет тенденцию к завышению).

Почему это важно:

  • В стабильном режиме работы приложения : значения min/avg/max близки друг к другу.
  • Утечка памяти : максимальное значение значительно выше минимального, а среднее значение постепенно увеличивается со временем по мере роста количества выборок.
  • Неравномерное использование : максимальное значение значительно выше среднего, что указывает на кратковременные ресурсоемкие операции (например, обработку изображений), которые могут привести к ошибке нехватки памяти, если выполняются в неподходящее время.

Состояния процесса

procstats отслеживает приложения в нескольких штатах (часто с сокращенным обозначением):

  • Persist : Критические системные процессы, которые работают постоянно.
  • Вверху : приложение в данный момент находится на переднем плане и видно пользователю.
  • ImpFg (Important Foreground): Критические фоновые процессы (например, IME) или приложения с фоновой службой.
  • ImpBg (Важные фоновые процессы): Фоновые процессы, которые считаются важными для системы.
  • Fgs / BFgs : службы переднего плана или связанные службы переднего плана.
  • Service / ServRst : Фоновые службы или службы, которые в данный момент перезапускаются.
  • Cached / LastAct : Приложения, которые кэшируются в фоновом режиме для быстрого перезапуска. Системе безопасно завершить их работу.

Состояние памяти устройства и статистика времени выполнения

procstats также отслеживает, сколько времени всё устройство провело под нагрузкой памяти. Обратите внимание на раздел «Статистика выполнения» :

Run time Stats:
  SOff/Norm: +1h10m15s
   SOn/Norm: +15m20s
        Low: +2m10s
       Crit: +45s
  • SOff / SOn : Экран выключен против экрана включен.
  • Нормальный / Умеренный / Низкий / Критический : Уровни нагрузки на память (Нормальный, Умеренный, Низкий, Критический).

Если устройство проводит значительное время в режимах низкой или критической нагрузки, оно будет работать медленно, поскольку ядро ​​активно освобождает память, а механизм завершения фоновых процессов (LMK) начинает завершать работу фоновых процессов. Затем вы можете просмотреть статистику по каждому пакету, чтобы увидеть, какие приложения используют много памяти именно тогда, когда устройство находится в этих стрессовых состояниях.


← Концепции | ↑ Вверх | Java →