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

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

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

8 ответов


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

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

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

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

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

переходя на более низкий уровень, вы можете использовать API отладки для получения необработанной информации об использовании памяти на уровне ядра: android.ОС.Отлаживать.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 в целом) фактически совместно использоваться несколькими процессами. Так что, сколько памяти использует процесс, на самом деле не ясно. Добавьте поверх этого подкачки на диск (не говоря уже о swap, который мы не используем на Android), и это еще менее ясно.

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


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

получить размер кучи 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


это незавершенная работа, но вот чего я не понимаю:

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


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

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


обновление:

статистика процесса, сервис, чтобы узнать, как ваше приложение управляет памятью, объясненной в блогеСтатистика Процесса: Понимание Того, Как Ваше Приложение Использует Рам!--4--> по Диана Hackborn:


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

enter image description here

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

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

enter image description here

Рис. 1. Форсирование события GC (сбор мусора) на мониторе памяти Android

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


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

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);

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

  • Runtime.getRuntime().totalMemory(): возвращает только память JVM
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() и все остальное, на основе /proc/meminfo - возвращает информацию о памяти обо всех объединенных процессах (например,android_util_Process.cpp)
  • Debug.getNativeHeapAllocatedSize() - использует mallinfo() которые возвращают информацию о выделениях памяти, выполняемых malloc() и только связанные функции (см. android_os_Debug.cpp)
  • Debug.getMemoryInfo() - делает работу, но это слишком медленно. Это занимает около 200ms on 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 метрика. Подробнее об этом вы можете узнать здесь:один, два и три.


П. С. я заметил, что тема по-прежнему имеет отсутствие фактического и простого фрагмента кода о том, как оценка использование памяти процесса, если производительность не является критическим требованием:

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;

есть много ответов выше, которые, безусловно, помогут вам, но (после 2 дней позволять и исследования на инструментах памяти АБР) я думаю, что могу помочь с моим мнение тоже.

As Hackbod говорит : так есть невозможно получить точный объем памяти для каждого процесса.

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

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

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

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

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

разрешение :

<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, который сложно хранить, поскольку вам нужно будет отделить его вручную.

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