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




утечка памяти android (7)

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

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

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

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

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

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

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


1) Полагаю, нет, по крайней мере, не от Java.
2)

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

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

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

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

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

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

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

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

разрешение:

<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:

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

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


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

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


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

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

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

Изменения API сервисов, начиная с Android 2.0

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

Находясь на более низком уровне, вы можете использовать API Debug для получения информации об уровне памяти на уровне ядра: 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, свободная куча для дополнительных распределений, и 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 из памяти настроен для конкретной системы, чтобы убедиться, что фоновые процессы убиты до того, как кэшированная ОЗУ будет слишком много потребляться ими, чтобы привести к такому поисковому вызову.


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

  • 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 . Вы можете найти более подробную информацию об этом здесь: one , two и three .

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;




memory-management