Как обнаружить использование памяти моим приложением в Android?

Кажется, что «AS» постороннее вблизи «AS TABLE_RESOLVERS»

782
задан Peter Mortensen 27 October 2014 в 21:25
поделиться

3 ответа

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

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

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

API службы меняются, начиная с Android 2.0

Сейчас ActivityManager.getMemoryInfo () - это наш API самого высокого уровня для просмотра общего использования памяти. В основном это помогает приложению оценить, насколько система приближается к тому, чтобы не иметь больше памяти для фоновых процессов, и, таким образом, необходимо начать убивать необходимые процессы, такие как службы. Для приложений, работающих только на Java, от этого мало толку, поскольку ограничение размера кучи Java отчасти используется для того, чтобы одно приложение не могло нагружать систему до этого момента.

Переходя на более низкий уровень, вы можете использовать Debug API для получения необработанной информации уровня ядра об использовании памяти: android.os.Debug.MemoryInfo

Примечание, начиная с версии 2.0, также существует API, ActivityManager.getProcessMemoryInfo , чтобы получить эту информацию о другом процессе: ActivityManager.getProcessMemoryInfo (int [])

Это возвращает низкоуровневую структуру MemoryInfo со всеми этими данными:

    /** The proportional set size for dalvik. */
    public int dalvikPss;
    /** The private dirty pages used by dalvik. */
    public int dalvikPrivateDirty;
    /** The shared dirty pages used by dalvik. */
    public int dalvikSharedDirty;

    /** The proportional set size for the native heap. */
    public int nativePss;
    /** The private dirty pages used by the native heap. */
    public int nativePrivateDirty;
    /** The shared dirty pages used by the native heap. */
    public int nativeSharedDirty;

    /** The proportional set size for everything else. */
    public int otherPss;
    /** The private dirty pages used by everything else. */
    public int otherPrivateDirty;
    /** The shared dirty pages used by everything else. */
    public int otherSharedDirty;

Но что касается разницы между Pss , PrivateDirty , и SharedDirty ... ну а теперь самое интересное.

Большая часть памяти в Android (и Linux в целом) фактически распределяется между несколькими процессами. Так что, сколько памяти использует процесс, на самом деле неясно. Добавьте поверх этой подкачки на диск (не говоря уже о свопе, который мы не используем на Android), и это станет еще менее ясным.

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

Число Pss - это метрика, вычисляемая ядром, которая учитывает совместное использование памяти - в основном каждая страница ОЗУ в процессе масштабируется пропорционально количеству других процессов, также использующих эту страницу. Таким образом, вы можете (теоретически) сложить pss для всех процессов, чтобы увидеть общий объем оперативной памяти, которую они используют, и сравнить pss между процессами, чтобы получить приблизительное представление об их относительном весе.

Другой интересный показатель - PrivateDirty , который, по сути, представляет собой объем ОЗУ внутри процесса, который не может быть выгружен на диск (он не поддерживается теми же данными на диске), и не является совместно с любыми другими процессами. Другой способ взглянуть на это - это оперативная память, которая станет доступной для системы, когда этот процесс завершится (и, вероятно, быстро попадет в кеши и другие способы ее использования).

Это почти все API SDK для этого. Однако вы как разработчик можете сделать со своим устройством больше.

Используя adb , вы можете получить много информации об использовании памяти работающей системой. Распространенной является команда adb shell dumpsys meminfo , которая выдаст кучу информации об использовании памяти каждым процессом Java, содержащую вышеупомянутую информацию, а также множество других вещей. Вы также можете указать имя или идентификатор отдельного процесса, чтобы увидеть, например adb shell dumpsys meminfo system предоставьте мне системный процесс:

** MEMINFO in pid 890 [system] **
                    native   dalvik    other    total
            size:    10940     7047      N/A    17987
       allocated:     8943     5516      N/A    14459
            free:      336     1531      N/A     1867
           (Pss):     4585     9282    11916    25783
  (shared dirty):     2184     3596      916     6696
    (priv dirty):     4504     5956     7456    17916

 Objects
           Views:      149        ViewRoots:        4
     AppContexts:       13       Activities:        0
          Assets:        4    AssetManagers:        4
   Local Binders:      141    Proxy Binders:      158
Death Recipients:       49
 OpenSSL Sockets:        0

 SQL
            heap:      205          dbFiles:        0
       numPagers:        0   inactivePageKB:        0
    activePageKB:        0

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

Если вы просто хотите посмотреть на использование памяти всеми процессами, вы можете использовать команду adb shell procrank . Результат этого в той же системе выглядит так:

  PID      Vss      Rss      Pss      Uss  cmdline
  890   84456K   48668K   25850K   21284K  system_server
 1231   50748K   39088K   17587K   13792K  com.android.launcher2
  947   34488K   28528K   10834K    9308K  com.android.wallpaper
  987   26964K   26956K    8751K    7308K  com.google.process.gapps
  954   24300K   24296K    6249K    4824K  com.android.phone
  948   23020K   23016K    5864K    4748K  com.android.inputmethod.latin
  888   25728K   25724K    5774K    3668K  zygote
  977   24100K   24096K    5667K    4340K  android.process.acore
...
   59     336K     332K      99K      92K  /system/bin/installd
   60     396K     392K      93K      84K  /system/bin/keystore
   51     280K     276K      74K      68K  /system/bin/servicemanager
   54     256K     252K      69K      64K  /system/bin/debuggerd

Здесь столбцы Vss и Rss в основном представляют собой шум (это прямое адресное пространство и использование ОЗУ процессом, где, если вы сложите использование ОЗУ между процессами, вы получите смехотворно большое число).

Pss - это то, что мы видели ранее, а Uss - Priv Dirty .

Здесь следует отметить интересную вещь: Pss и Uss немного (или более чем немного) отличаются от того, что мы видели в meminfo . Это почему?Ну, procrank использует другой механизм ядра для сбора данных, чем meminfo , и они дают несколько другие результаты. Это почему? Честно говоря, я понятия не имею. Я считаю, что procrank может быть более точным ... но на самом деле это просто оставляет суть: «относитесь к любой информации из памяти, которую вы получаете с долей скептицизма; часто с очень большой зернистостью».

Наконец, есть команда adb shell cat / proc / meminfo , которая дает сводку об общем использовании памяти системой. Здесь много данных, только первые несколько цифр заслуживают обсуждения (а остальные понятны немногим, и мои вопросы к тем немногим людям о них часто приводят к противоречивым объяснениям):

MemTotal:         395144 kB
MemFree:          184936 kB
Buffers:             880 kB
Cached:            84104 kB
SwapCached:            0 kB

MemTotal - это общий объем памяти, доступной ядру и пользовательскому пространству (часто меньше, чем фактическая физическая RAM устройства, поскольку часть этой RAM необходима для радио, буферов DMA и т. д.).

MemFree - это объем ОЗУ, который вообще не используется. Число, которое вы видите здесь, очень велико; обычно в системе Android это всего несколько МБ, так как мы пытаемся использовать доступную память для поддержания работы процессов

Кэширование - это ОЗУ, используемое для кэшей файловой системы и других подобных вещей. В обычных системах для этого потребуется около 20 МБ, чтобы избежать плохого состояния подкачки; Убийца нехватки памяти Android настроен для конкретной системы, чтобы гарантировать, что фоновые процессы завершаются до того, как кэшированная оперативная память будет использована ими слишком много, чтобы привести к такой подкачке страниц.

1002
ответ дан 22 November 2019 в 21:21
поделиться

1) Думаю, что нет, по крайней мере, не из Java.
2)

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
16
ответ дан 22 November 2019 в 21:21
поделиться

Эта работа продолжается, но я не понимаю этого:

ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);

Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );

List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();

Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
    pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}

Collection<Integer> keys = pidMap.keySet();

for(int key : keys)
{
    int pids[] = new int[1];
    pids[0] = key;
    android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
    for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
    {
        Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
        Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
    }
}

Почему PID не отображается на результат в activityManager.getProcessMemoryInfo ()? Очевидно, вы хотите сделать полученные данные значимыми, так почему же Google так усложнил корреляцию результатов? Текущая система даже не работает хорошо, если я хочу обработать все использование памяти, поскольку возвращаемый результат представляет собой массив объектов android.os.Debug.MemoryInfo, но ни один из этих объектов на самом деле не сообщает вам, с какими pid они связаны. Если вы просто передадите массив всех pid, у вас не будет возможности понять результаты. Насколько я понимаю, его использование делает бессмысленным передачу более чем одного pid за раз, а затем, если это так, зачем делать так, чтобы activityManager.getProcessMemoryInfo () принимал только массив int?

51
ответ дан 22 November 2019 в 21:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: