Как обнаружить использование памяти моим приложением в Android? - PullRequest
781 голосов
/ 19 февраля 2010

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

Я надеюсь, что есть способ сделать это. Плюс как мне тоже получить свободную память телефона?

Ответы [ 9 ]

997 голосов
/ 20 февраля 2010

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

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

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

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

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

Переходя на более низкий уровень, вы можете использовать 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, содержащую вышеупомянутую информацию, а также множество других вещей. Вы также можете указать имя или pid отдельного процесса, например, 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

Верхний раздел является основным, где size - это общий размер в адресном пространстве конкретной кучи, allocated - это килобайт фактических выделений, которые, как думает куча, имеет, free - это оставшиеся свободные килобайты.куча предназначена для дополнительных выделений, и 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 - это общий объем памяти, доступной ядру и пользовательскому пространству (часто меньше, чем фактическая физическая память устройства, поскольку часть этой памяти требуется для радиосвязи, буферов DMA и т. Д.).

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

Cached - это оперативная память, используемая для кэширования файловой системы и других подобных вещей.Типичные системы должны иметь около 20 МБ или около того, чтобы избежать перехода в состояние плохой подкачки;Android out of memory killer настроен для конкретной системы, чтобы гарантировать, что фоновые процессы будут убиты до того, как кэшированная оперативная память израсходует ими слишком много, чтобы привести к такой подкачке.

76 голосов
/ 01 декабря 2012

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

Получите размер кучи виртуальной машины по телефону:

Runtime.getRuntime().totalMemory();

Получить выделенную память виртуальной машины, позвонив по номеру:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Получить ограничение размера кучи виртуальной машины можно по телефону:

Runtime.getRuntime().maxMemory()

Получить собственную выделенную память, вызвав:

Debug.getNativeHeapAllocatedSize();

Я создал приложение, чтобы выяснить поведение OutOfMemoryError и отслеживать использование памяти.

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

Вы можете получить исходный код по адресу https://github.com/coocood/oom-research

51 голосов
/ 13 августа 2010

Это работа, но я не понимаю:

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, но ни один из этих объектов на самом деле не говорит вам, с какими пидами они связаны. Если вы просто передадите массив всех pids, у вас не будет возможности понять результаты. Как я понимаю, его использование делает бессмысленным передачу более одного pid за раз, а затем, если это так, зачем делать так, чтобы ActivityManager.getProcessMemoryInfo () принимает только массив int?

24 голосов
/ 21 декабря 2011

Hackbod's - один из лучших ответов на переполнение стека. Это проливает свет на очень непонятный предмет. Это мне очень помогло.

Еще один действительно полезный ресурс - это видео, которое обязательно нужно посмотреть: Google I / O 2011: управление памятью для приложений Android


UPDATE:

Process Stats, сервис, позволяющий узнать, как ваше приложение управляет памятью, объяснено в блоге Process Stats: Понимание того, как ваше приложение использует оперативную память от Dianne Hackborn:

19 голосов
/ 12 августа 2015

Android Studio 0.8.10+ представила невероятно полезный инструмент под названием Монитор памяти .

enter image description here

Для чего он хорош:

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

enter image description here

Рисунок 1.Принудительное событие GC (Сборка мусора) на Android Memory Monitor

Используя его, вы можете получить много полезной информации о потреблении оперативной памяти вашего приложения в реальном времени.

16 голосов
/ 19 февраля 2010

1) Наверное, нет, по крайней мере, с Java.
2)

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
5 голосов
/ 14 августа 2017

Мы выяснили, что у всех стандартных способов получения общего объема памяти текущего процесса есть некоторые проблемы.

  • Runtime.getRuntime().totalMemory(): возвращает только память JVM
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() и все остальное, основанное на /proc/meminfo - возвращает информацию в памяти обо всех объединенных процессах (например, android_util_Process.cpp )
  • Debug.getNativeHeapAllocatedSize() - использует mallinfo(), который возвращает информацию о распределении памяти, выполняемом только malloc() и соответствующими функциями (см. android_os_Debug.cpp )
  • Debug.getMemoryInfo() - выполняет работу, но это слишком медленно. Для одного звонка требуется около 200 мс на Nexus 6 . Снижение производительности делает эту функцию бесполезной для нас, поскольку мы регулярно ее вызываем, и каждый вызов довольно заметен (см. android_os_Debug.cpp )
  • ActivityManager.getProcessMemoryInfo(int[]) - внутренние вызовы Debug.getMemoryInfo() (см. ActivityManagerService.java )

Наконец, мы использовали следующий код:

const long pageSize = 4 * 1024; //`sysconf(_SC_PAGESIZE)`
string stats = File.ReadAllText("/proc/self/statm");
var statsArr = stats.Split(new [] {' ', '\t', '\n'}, 3);

if( statsArr.Length < 2 )
    throw new Exception("Parsing error of /proc/self/statm: " + stats);

return long.Parse(statsArr[1]) * pageSize;

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


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

Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
long res = memInfo.getTotalPrivateDirty();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
    res += memInfo.getTotalPrivateClean(); 

return res * 1024L;
0 голосов
/ 20 февраля 2019

В Android Studio 3.0 они представили Android-Profiler, чтобы помочь вам понять, как ваше приложение использует ресурсы процессора, памяти, сети и батареи.enter image description here

0 голосов
/ 20 февраля 2018

Есть много ответов выше, которые определенно помогут вам, но (после 2 дней использования и изучения инструментов памяти adb) Я думаю, что могу помочь с моим мнением тоже .

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

Но вы можете приблизиться к этому по некоторой логике ... и я расскажу, как ..

Есть некоторые API, такие как android.os.Debug.MemoryInfo и ActivityManager.getMemoryInfo(), упомянутые выше, о которых вы, возможно, уже читали и использовали, но я расскажу о другом способе

Итак, для начала вам нужно быть пользователем root. Войдите в консоль с привилегией root, выполнив su в процессе и получите ее output and input stream. Затем передайте id\n (введите) в ouputstream и запишите его в выходной процесс, если получите входной поток, содержащий uid=0, , вы являетесь пользователем root.

Теперь вот логика, которую вы будете использовать в вышеописанном процессе

Когда вы получаете выходной поток процесса , передайте команду (procrank, dumpsys meminfo и т. Д.) С \n вместо id и получите его inputstream и прочитайте, сохраните поток в байтах [ ], char [] и т. д. используйте raw data .. и все готово !!!!!

разрешение:

<uses-permission android:name="android.permission.FACTORY_TEST"/>

Проверьте, являетесь ли вы пользователем root:

// su command to get root access
Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
DataInputStream dataInputStream = 
                           new DataInputStream(process.getInputStream());
if (dataInputStream != null && dataOutputStream != null) {
   // write id to console with enter
   dataOutputStream.writeBytes("id\n");                   
   dataOutputStream.flush();
   String Uid = dataInputStream.readLine();
   // read output and check if uid is there
   if (Uid.contains("uid=0")) {                           
      // you are root user
   } 
}

Выполните вашу команду с su

Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
if (dataOutputStream != null) {
 // adb command
 dataOutputStream.writeBytes("procrank\n");             
 dataOutputStream.flush();
 BufferedInputStream bufferedInputStream = 
                     new BufferedInputStream(process.getInputStream());
 // this is important as it takes times to return to next line so wait
 // else you with get empty bytes in buffered stream 
 try {
       Thread.sleep(10000);
 } catch (InterruptedException e) {                     
       e.printStackTrace();
 }
 // read buffered stream into byte,char etc.
 byte[] bff = new byte[bufferedInputStream.available()];
 bufferedInputStream.read(bff);
 bufferedInputStream.close();
 }
}

logcat: result

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

Это просто попытка, пожалуйста, предложите мне, если я что-то пропустил

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...