Как программно найти память, используемую в приложении Android?
Я надеюсь, что есть способ сделать это. Плюс, как мне получить свободную память телефона тоже?
Как программно найти память, используемую в приложении Android?
Я надеюсь, что есть способ сделать это. Плюс, как мне получить свободную память телефона тоже?
Ответы:
Обратите внимание, что использование памяти в современных операционных системах, таких как 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, фактически сопоставленную каждому процессу, и суммировали все процессы, вы, вероятно, в конечном итоге получили бы число, намного превышающее фактическую общую RAM.
Pss
Число является метрикой ядра вычисляет , которая принимает во совместный счете памяти - в основном каждой страницу памяти в процессе масштабируется отношением числа других процессов , также используя эту страницу. Таким образом, вы можете (теоретически) сложить pss во всех процессах, чтобы увидеть общую оперативную память, которую они используют, и сравнить pss между процессами, чтобы получить приблизительное представление об их относительном весе.
Другой интересный показатель здесь - PrivateDirty
это объем оперативной памяти внутри процесса, который не может быть перенесен на диск (он не поддерживается теми же данными на диске) и не используется совместно с другими процессами. Еще один способ взглянуть на это - это оперативная память, которая станет доступной для системы, когда этот процесс завершится (и, вероятно, будет быстро отнесен к кэшам и другим видам ее использования).
Это в значительной степени SDK API для этого. Тем не менее, вы можете сделать больше как разработчик своего устройства.
Используя adb
, вы можете получить много информации об использовании памяти работающей системой. Распространенной является команда, adb shell dumpsys meminfo
которая будет выдавать кучу информации об использовании памяти каждым процессом Java, содержащую вышеупомянутую информацию, а также множество других вещей. Вы также можете указать имя или pid отдельного процесса, например, adb shell dumpsys meminfo system
дать мне системный процесс:
** MEMINFO в pid 890 [система] ** родной далвик другой итого размер: 10940 7047 N / A 17987 выделено: 8943 5516 н / д 14459 бесплатно: 336 1531 N / A 1867 (Pss): 4585 9282 11916 25783 (общий грязный): 2184 3596 916 6696 (конфиденциально): 4504 5956 7456 17916 Объекты Просмотров: 149 ViewRoots: 4 AppContexts: 13 Активности: 0 Активы: 4 Актив Менеджеры: 4 Местные связующие: 141 прокси Связующие: 158 Получатели смерти: 49 Разъемы OpenSSL: 0 SQL куча: 205 дБфайлов: 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 зигота 977 24100K 24096K 5667K 4340K android.process.acore ... 59 336K 332K 99K 92K / system / bin / installd 60 396K 392K 93K 84K / система / корзина / склад ключей 51 280K 276K 74K 68K / система / 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 кБ MemFree: 184936 кБ Буферы: 880 кБ Кэшировано: 84104 кБ SwapCached: 0 кБ
MemTotal
это общий объем памяти, доступной ядру и пользовательскому пространству (часто меньше, чем фактическая физическая память устройства, так как часть этой памяти необходима для радиосвязи, буферов DMA и т. д.).
MemFree
это объем оперативной памяти, который не используется вообще. Число, которое вы видите здесь, очень высоко; как правило, в системе Android это будет всего лишь несколько МБ, так как мы стараемся использовать доступную память для поддержания процессов в рабочем состоянии
Cached
ОЗУ используется для кэшей файловой системы и других подобных вещей. Типичные системы должны иметь около 20 МБ или около того, чтобы избежать перехода в состояние плохой подкачки; Android out of memory killer настроен для конкретной системы, чтобы гарантировать, что фоновые процессы будут убиты до того, как кэшированная оперативная память израсходует ими слишком много, чтобы привести к такой подкачке.
Да, вы можете получить информацию о памяти программным путем и решить, следует ли выполнять объемную работу с памятью.
Получить размер кучи виртуальной машины можно по телефону:
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
Это работа в процессе, но это то, что я не понимаю:
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 они связаны. Если вы просто передадите массив всех pids, у вас не будет возможности понять результаты. Насколько я понимаю, его использование делает бессмысленным передачу более одного pid за раз, а затем, если это так, зачем делать так, чтобы activityManager.getProcessMemoryInfo () принимает только массив int?
Hackbod's - один из лучших ответов на переполнение стека. Это проливает свет на очень непонятный предмет. Это мне очень помогло.
Еще один действительно полезный ресурс - это видео, которое обязательно нужно посмотреть: Google I / O 2011: управление памятью для приложений Android
ОБНОВИТЬ:
Process Stats, сервис, позволяющий узнать, как ваше приложение управляет памятью, объясняется в посте блога. Process Stats: Понимание того, как ваше приложение использует оперативную память от Dianne Hackborn:
Android Studio 0.8.10+ представила невероятно полезный инструмент под названием Memory Monitor .
Для чего это хорошо:
- Отображение доступной и используемой памяти в виде графика и событий сборки мусора с течением времени.
- Быстрая проверка, может ли медлительность приложения быть связана с чрезмерными событиями сборки мусора.
- Быстрая проверка, может ли сбой приложения быть связан с нехваткой памяти.
Рисунок 1. Принудительное событие GC (Сборка мусора) на Android Memory Monitor
Используя его, вы можете получить много полезной информации о потреблении оперативной памяти вашего приложения.
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()
: возвращает только память JVMActivityManager.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;
В Android Studio 3.0 они представили Android-Profiler, чтобы помочь вам понять, как ваше приложение использует ресурсы процессора, памяти, сети и батареи.
https://developer.android.com/studio/profile/android-profiler
Выше приведено много ответов, которые определенно помогут вам, но (после 2 дней использования и изучения инструментов памяти adb) я думаю, что тоже могу помочь с моим мнением .
Как говорит Хэкбод: Таким образом, если бы вы взяли всю физическую RAM, фактически сопоставленную с каждым процессом, и суммировали все процессы, вы, вероятно, в итоге получили бы число, намного превышающее фактическую общую RAM. таким образом, вы не сможете получить точное количество памяти на процесс.
Но вы можете приблизиться к этому по некоторой логике .. и я расскажу, как ..
Есть некоторые API, подобные
android.os.Debug.MemoryInfo
иActivityManager.getMemoryInfo()
упомянутые выше, о которых вы, возможно, уже читали и использовали, но я расскажу о другом
Итак, во-первых, вам нужно быть пользователем root, чтобы все заработало. Войдите в консоль с правами суперпользователя, выполнив su
процесс, и получите его output and input stream
. Затем передайте id\n
(введите) в ouputstream и запишите его для обработки выходных данных. Если вы получите входной поток, содержащий uid=0
, вы являетесь пользователем root.
Теперь вот логика, которую вы будете использовать в процессе выше
Когда вы получаете ouputstream процесса, передайте свою команду (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();
}
}
Вы получаете необработанные данные в единственной строке из консоли, а не в каком-то случае из любого API, который сложно хранить, так как вам придется отделить его вручную .
Это всего лишь попытка, пожалуйста, предложите мне, если я что-то пропустил