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

728

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

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

Теги:
memory
memory-management

8 ответов

908
Лучший ответ

Обратите внимание, что использование памяти в современных операционных системах, таких как 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), и это еще менее понятно.

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

Число Pss - это метрика, которую вычисляет ядро, которое учитывает совместное использование памяти - в основном каждая страница ОЗУ в процессе масштабируется соотношением количества других процессов, также использующих эту страницу. Таким образом, вы можете (теоретически) добавить pss во все процессы, чтобы увидеть общую RAM, которую они используют, и сравнить 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 - это kb фактических распределений, которые куча думает, что он имеет, free - оставшийся kb 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 из памяти настроен для конкретной системы, чтобы убедиться, что фоновые процессы убиты до того, как кэшированная ОЗУ будет слишком много потребляться ими, чтобы привести к такому поисковому вызову.

  • 1
    Посмотрите на pixelbeat.org/scripts/ps_mem.py, который использует методы, упомянутые выше, чтобы показать используемую оперативную память для программ
  • 16
    Очень красиво написано! Я написал пост об управлении памятью и использовании различных инструментов для проверки вашего использования кучи здесь macgyverdev.blogspot.com/2011/11/… если кто-нибудь найдет это полезным.
Показать ещё 7 комментариев
65

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

Получить размер кучи VM, вызвав:

Runtime.getRuntime().totalMemory();

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

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

Получить ограничение размера кучи VM, вызвав:

Runtime.getRuntime().maxMemory()

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

Debug.getNativeHeapAllocatedSize();

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

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

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

  • 7
    Эта среда выполнения вернет использование памяти текущим процессом или общей системной кучей?
  • 1
    @mahemadhi из JavaDoc метода totalMemory () «Возвращает общий объем памяти, доступный для работающей программы»
Показать ещё 1 комментарий
47

Это незавершенное производство, но этого я не понимаю:

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?

  • 2
    Скорее всего, они в том же порядке, что и входной массив.
  • 2
    Это кажется очень не интуитивным способом делать вещи. Да, это, вероятно, так, но каким образом это ООП?
Показать ещё 6 комментариев
24

Hackbod - один из лучших ответов на Stack Overflow. Он проливает свет на очень неясную тему. Это очень помогло мне.

Другим очень полезным ресурсом является обязательное видео: Google I/O 2011: управление памятью для приложений Android


UPDATE:

Статистика процесса, служба, чтобы узнать, как ваше приложение управляет памятью, объясняется в сообщении блога Статистика процесса: понимание того, как ваше приложение использует ОЗУ от Dianne Hackborn:

16

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

Изображение 1395

Что это хорошо для:

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

Изображение 1396

Рисунок 1. Принуждение события GC (сборка мусора) на мониторе Android Memory

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

  • 0
    хороший способ сделать это автоматически на CI сервере;)
16

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

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
  • 1
    замените на (ActivityManager activityManager = (ActivityManager) getSystemService (ACTIVITY_SERVICE);) вместо (ActivityManager ActivityManager = = (ActivityManager) getSystemService (ACTIVITY_SERVICE);)
2

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

  • 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

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

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

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

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

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

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

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

:

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

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

try {
    // 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")) {                           
             // yes sir you are root user
         } 
    }
} catch (Exception e) {
}

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

try {
      // 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();
      }
} catch (Exception e) {
}

logcat: Изображение 1397

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

Это просто попытка, пожалуйста, поправьте меня, если я ошибаюсь

Ещё вопросы

Сообщество Overcoder
Наверх
Меню