Как я могу найти память, используемую в приложении для 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, фактически отображаемую в каждом процессе, и добавить все процессы, вы, вероятно, в конечном итоге получили бы число, намного большее, чем фактическая общая оперативная память.
Число 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 из памяти настроен для конкретной системы, чтобы убедиться, что фоновые процессы убиты до того, как кэшированная ОЗУ будет слишком много потребляться ими, чтобы привести к такому поисковому вызову.
Да, вы можете получить информацию о памяти программно и решить, следует ли делать интенсивную работу с памятью.
Получить размер кучи 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?
Hackbod - один из лучших ответов на Stack Overflow. Он проливает свет на очень неясную тему. Это очень помогло мне.
Другим очень полезным ресурсом является обязательное видео: Google I/O 2011: управление памятью для приложений Android
UPDATE:
Статистика процесса, служба, чтобы узнать, как ваше приложение управляет памятью, объясняется в сообщении блога Статистика процесса: понимание того, как ваше приложение использует ОЗУ от Dianne Hackborn:
Android Studio 0.8.10+ представила невероятно полезный инструмент под названием Монитор памяти.
Что это хорошо для:
- Отображение доступной и используемой памяти на графике и сборка мусора событий со временем.
- Быстрое тестирование того, может ли медленность приложения связанных с чрезмерными событиями сбора мусора.
- Быстрое тестирование могут ли проблемы со сбоями приложений быть исчерпаны.
Рисунок 1. Принуждение события GC (сборка мусора) на мониторе Android Memory
У вас может быть много хорошей информации о потреблении оперативной памяти приложения в режиме реального времени, используя его.
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;
Выше приведено много ответов, которые определенно помогут вам, но (после 2 дней использования и изучения инструментов памяти adb) я думаю, что тоже могу помочь с моим мнением.
Как говорит Хэкбод: Таким образом, если бы вы взяли все физическое ОЗУ, фактически сопоставленное каждому процессу, и суммировали все процессы, вы, вероятно, в итоге получили бы число, намного превышающее фактическое общее ОЗУ. таким образом, вы не сможете получить точное количество памяти на процесс.
Но вы можете приблизиться к этому по некоторой логике.. и я расскажу, как..
Есть некоторые API, такие как
android.os.Debug.MemoryInfo
иActivityManager.getMemoryInfo()
упомянутые выше, о которых вы, возможно, уже читали и использовали, но я расскажу о другом способе
Итак, во-первых, вам нужно быть пользователем root, чтобы все заработало. Войдите в консоль с правами суперпользователя, выполнив su
в процессе, и получите его output and input stream
. Затем передайте id\n
(enter) в 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, который сложно хранить, так как вам придется отделить его вручную.
Это всего лишь попытка, пожалуйста, предложите мне, если я что-то пропустил
В Android Studio 3.0 они представили Android-Profiler, чтобы помочь вам понять, как ваше приложение использует ресурсы процессора, памяти, сети и батареи.
https://developer.android.com/studio/profile/android-profiler