Как мне определить использование памяти моим приложением в Android?
Как я могу программно определить, какая память используется в моем приложении для Android?
Я надеюсь, что есть способ сделать это. Кроме того, как мне получить свободную память телефона?
Переведено автоматически
Ответ 1
Обратите внимание, что использование памяти в современных операционных системах, таких как Linux, является чрезвычайно сложной областью, которую трудно понять. Фактически, шансы на то, что вы действительно правильно интерпретируете любые полученные вами цифры, крайне низки. (Практически каждый раз, когда я смотрю цифры использования памяти с другими инженерами, всегда возникает долгая дискуссия о том, что они на самом деле означают, которая приводит лишь к расплывчатому выводу.)
Примечание: теперь у нас есть гораздо более обширная документация по управлению памятью вашего приложения, которая охватывает большую часть приведенного здесь материала и более актуальна для Android.
Первым делом, вероятно, стоит прочитать последнюю часть этой статьи, в которой обсуждается, как управляется память на Android:
Изменения Service 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-процессом, содержащая вышеуказанную информацию, а также множество других вещей. Вы также можете указать имя или pid отдельного процесса, чтобы увидеть, например, adb shell dumpsys meminfo system
дайте мне системный процесс:
** MEMINFO в pid 890 [system] **
всего на родном dalvik other
размер: 10940 7047 N / A 17987
выделено: 8943 5516 N / A 14459
свободно: 336 1531 N / A 1867
(Pss): 4585 9282 11916 25783
(общий грязный): 2184 3596 916 6696
(приватный грязный): 4504 5956 7456 17916
Объекты
Просмотров: 149 Просмотров: 4
AppContexts: 13 Activities: 0
Активы: 4 AssetManagers: 4
Локальные связующие: 141 Прокси-связующие: 158
Получатели смерти: 49
Сокеты OpenSSL: 0
SQL
куча: 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 / хранилище ключей
51 280K 276K 74K 68K / system / bin / servicemanager
54 256K 252K 69 K 64K / system / bin / debuggerd
Здесь столбцы Vss
и Rss
в основном представляют собой шум (это прямое адресное пространство и использование оперативной памяти процессом, где, если вы сложите использование оперативной памяти между процессами, вы получите смехотворно большое число).
Pss
это то, что мы видели раньше, и Uss
есть Priv Dirty
.
Здесь следует отметить интересную вещь: Pss
и Uss
немного (или более чем немного) отличаются от того, что мы видели в meminfo
. Почему это? Ну, procrank использует другой механизм ядра для сбора своих данных, чем meminfo
делает, и они дают несколько иные результаты. Почему это? Честно говоря, я понятия не имею. Я полагаю, что procrank
может быть более точным... но на самом деле, это просто оставляет точку зрения: "принимайте любую информацию о памяти, которую вы получаете, с долей скептицизма; часто с очень большой долей скептицизма".
Наконец, есть команда adb shell cat /proc/meminfo
, которая дает сводную информацию об общем использовании памяти системой. Здесь много данных, только первые несколько цифр заслуживают обсуждения (а остальные понятны немногим людям, и мои вопросы к этим немногим людям о них часто приводят к противоречивым объяснениям):
Общий объем памяти: 395144 кБ
MEMBEFREE: 184936 кБ
Буферы: 880 кБ
Кэш: 84104 кБ
SwapCached: 0 кБ
MemTotal
это общий объем памяти, доступный ядру и пользовательскому пространству (часто меньше, чем фактическая физическая оперативная память устройства, поскольку часть этой оперативной памяти необходима для радио, буферов DMA и т.д.).
MemFree
это объем оперативной памяти, который вообще не используется. Число, которое вы видите здесь, очень велико; обычно в системе Android это всего несколько МБ, поскольку мы стараемся использовать доступную память для поддержания процессов в рабочем состоянии.
Cached
используется ли оперативная память для кэшей файловой системы и других подобных вещей. Типичным системам потребуется около 20 МБ для этого, чтобы избежать сбоев в состоянии подкачки; Android out of memory killer настроен для конкретной системы, чтобы убедиться, что фоновые процессы будут остановлены до того, как они израсходуют слишком много кэшированной оперативной памяти, чтобы привести к такой подкачке.
Ответ 2
Да, вы можете получить информацию о памяти программно и решить, стоит ли выполнять работу с большим объемом памяти.
Получить размер кучи виртуальной машины, вызвав:
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
Ответ 3
Эта работа продолжается, но вот чего я не понимаю:
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?
Ответ 4
Hackbod - один из лучших ответов на Stack Overflow. Он проливает свет на очень неясную тему. Он мне очень помог.
Еще один действительно полезный ресурс - это обязательное к просмотру видео: Google I / O 2011: управление памятью для приложений Android
Обновить:
Статистика процессов, сервис для определения того, как ваше приложение управляет памятью, описан в сообщении в блоге Статистика процессов: понимание того, как ваше приложение использует оперативную память Дайанн Хакборн: