¿Cómo puedo descubrir el uso de memoria de mi aplicación en Android?
¿Cómo puedo encontrar la memoria utilizada en mi aplicación de Android mediante programación?
Espero que haya una manera de hacerlo. Además, ¿cómo obtengo también la memoria libre del teléfono?
Tenga en cuenta que el uso de la memoria en sistemas operativos modernos como Linux es un área extremadamente complicada y difícil de entender. De hecho, las posibilidades de que interpretes correctamente los números que obtengas son extremadamente bajas. (Prácticamente cada vez que analizo las cifras de uso de memoria con otros ingenieros, siempre hay una larga discusión sobre lo que realmente quieren decir, que solo da como resultado una conclusión vaga).
Nota: ahora tenemos documentación mucho más extensa sobre cómo administrar la memoria de su aplicación que cubre gran parte del material aquí y está más actualizada con el estado de Android.
Lo primero es probablemente leer la última parte de este artículo, que analiza cómo se administra la memoria en Android:
Cambios en la API de servicio a partir de Android 2.0
Ahora ActivityManager.getMemoryInfo()
es nuestra API de más alto nivel para analizar el uso general de la memoria. Esto está ahí principalmente para ayudar a una aplicación a evaluar qué tan cerca está el sistema de no tener más memoria para procesos en segundo plano, por lo que necesita comenzar a eliminar procesos necesarios, como servicios. Para aplicaciones Java puras, esto debería ser de poca utilidad, ya que el límite de almacenamiento dinámico de Java existe en parte para evitar que una aplicación pueda estresar el sistema hasta este punto.
Al ir a un nivel inferior, puede usar la API de depuración para obtener información sin procesar a nivel del kernel sobre el uso de la memoria: android.os.Debug.MemoryInfo
Tenga en cuenta que a partir de 2.0 también hay una API ActivityManager.getProcessMemoryInfo
para obtener esta información sobre otro proceso: ActivityManager.getProcessMemoryInfo(int[])
Esto devuelve una estructura MemoryInfo de bajo nivel con todos estos datos:
/** 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;
Pero en cuanto a cuál es la diferencia entre Pss
, PrivateDirty
y SharedDirty
... bueno, ahora comienza la diversión.
Gran parte de la memoria en Android (y en los sistemas Linux en general) se comparte entre múltiples procesos. Por lo tanto, no está claro cuánta memoria utiliza un proceso. Agregue además esa paginación al disco (y mucho menos el intercambio que no usamos en Android) y es aún menos claro.
Por lo tanto, si tomara toda la RAM física realmente asignada a cada proceso y sumara todos los procesos, probablemente terminaría con un número mucho mayor que la RAM total real.
El Pss
número es una métrica que calcula el kernel y que tiene en cuenta el uso compartido de memoria; básicamente, cada página de RAM en un proceso se escala según una proporción del número de otros procesos que también usan esa página. De esta manera, puede (en teoría) sumar el pss de todos los procesos para ver la RAM total que están utilizando y comparar el pss entre procesos para tener una idea aproximada de su peso relativo.
La otra métrica interesante aquí es PrivateDirty
, que es básicamente la cantidad de RAM dentro del proceso que no se puede paginar en el disco (no está respaldada por los mismos datos en el disco) y no se comparte con ningún otro proceso. Otra forma de ver esto es la RAM que estará disponible para el sistema cuando ese proceso desaparezca (y probablemente rápidamente incluida en cachés y otros usos de la misma).
Esas son prácticamente las API del SDK para esto. Sin embargo, hay más que puedes hacer como desarrollador con tu dispositivo.
Al utilizar adb
, puede obtener mucha información sobre el uso de la memoria de un sistema en ejecución. Uno común es el comando adb shell dumpsys meminfo
que generará un montón de información sobre el uso de la memoria de cada proceso Java, que contiene la información anterior, así como una variedad de otras cosas. También puedes agregar el nombre o pid de un solo proceso para ver, por ejemplo, adb shell dumpsys meminfo system
dame el proceso del sistema:
** MEMINFO en pid 890 [sistema] ** nativo dalvik otro total Tamaño: 10940 7047 N/D 17987 asignado: 8943 5516 N/A 14459 gratis: 336 1531 N/A 1867 (Pss): 4585 9282 11916 25783 (sucio compartido): 2184 3596 916 6696 (priv sucio): 4504 5956 7456 17916 Objetos Vistas: 149 Vistas Raíces: 4 AppContexts: 13 Actividades: 0 Activos: 4 AssetManagers: 4 Carpetas locales: 141 Carpetas proxy: 158 Destinatarios por fallecimiento: 49 Conectores OpenSSL: 0 SQL montón: 205 dbArchivos: 0 numPagers: 0 Página inactivaKB: 0 páginaactivaKB: 0
La sección superior es la principal, donde size
está el tamaño total en el espacio de direcciones de un montón en particular, allocated
son los kb de asignaciones reales que el montón cree que tiene, free
son los kb restantes libres que tiene el montón para asignaciones adicionales y pss
son priv dirty
los mismos. como se analizó anteriormente específicamente para las páginas asociadas con cada uno de los montones.
Si solo desea ver el uso de memoria en todos los procesos, puede usar el comando adb shell procrank
. La salida de esto en el mismo sistema se ve así:
Línea cmd PID Vss Rss Pss Uss 890 84456K 48668K 25850K 21284K servidor_sistema 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.teléfono 948 23020K 23016K 5864K 4748K com.android.inputmethod.latin 888 25728K 25724K 5774K 3668K cigoto 977 24100K 24096K 5667K 4340K android.proceso.acore ... 59 336K 332K 99K 92K /sistema/bin/instalado 60 396K 392K 93K 84K /sistema/bin/almacén de claves 51 280K 276K 74K 68K /sistema/bin/administrador de servicio 54 256K 252K 69K 64K /sistema/bin/depurado
Aquí las columnas Vss
y Rss
son básicamente ruido (estos son el espacio de direcciones sencillo y el uso de RAM de un proceso, donde si sumas el uso de RAM entre procesos obtienes un número ridículamente grande).
Pss
es como hemos visto antes, y Uss
es Priv Dirty
.
Algo interesante a tener en cuenta aquí: Pss
y Uss
son ligeramente (o más que ligeramente) diferentes de lo que vimos en meminfo
. ¿Porqué es eso? Bueno, procrank utiliza un mecanismo de kernel diferente al que meminfo
utiliza para recopilar sus datos, y dan resultados ligeramente diferentes. ¿Porqué es eso? Sinceramente no tengo ni idea. Creo que procrank
puede ser la más precisa... pero en realidad, esto simplemente deja el punto: "tome cualquier información de memoria que obtenga con un grano de sal; a menudo un grano muy grande".
Finalmente está el comando adb shell cat /proc/meminfo
que brinda un resumen del uso general de la memoria del sistema. Hay muchos datos aquí, solo los primeros números que vale la pena discutir (y los restantes son entendidos por pocas personas, y mis preguntas a esas pocas personas sobre ellos a menudo resultan en explicaciones contradictorias):
Memoria total: 395144 kB Libre de memoria: 184936 kB Búfers: 880 kB En caché: 84104 kB Intercambio en caché: 0 kB
MemTotal
es la cantidad total de memoria disponible para el kernel y el espacio del usuario (a menudo menos que la RAM física real del dispositivo, ya que parte de esa RAM se necesita para la radio, los buffers DMA, etc.).
MemFree
es la cantidad de RAM que no se utiliza en absoluto. El número que ves aquí es muy alto; Normalmente, en un sistema Android, esto sería solo unos pocos MB, ya que intentamos usar la memoria disponible para mantener los procesos en ejecución.
Cached
¿Es la RAM que se utiliza para los cachés del sistema de archivos y otras cosas similares? Los sistemas típicos necesitarán tener aproximadamente 20 MB para evitar entrar en malos estados de paginación; El eliminador de memoria insuficiente de Android está optimizado para un sistema en particular para garantizar que los procesos en segundo plano se eliminen antes de que consuman demasiado la RAM almacenada en caché para provocar dicha paginación.
Sí, puede obtener información de la memoria mediante programación y decidir si desea realizar un trabajo intensivo en memoria.
Obtenga el tamaño del montón de VM llamando a:
Runtime.getRuntime().totalMemory();
Obtenga memoria de VM asignada llamando a:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Obtenga el límite de tamaño del montón de VM llamando a:
Runtime.getRuntime().maxMemory()
Obtenga memoria asignada nativa llamando al:
Debug.getNativeHeapAllocatedSize();
Creé una aplicación para descubrir el comportamiento de OutOfMemoryError y monitorear el uso de la memoria.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Puede obtener el código fuente en https://github.com/coocood/oom-research
Este es un trabajo en progreso, pero esto es lo que no entiendo:
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");
}
}
¿Por qué no se asigna el PID al resultado en ActivityManager.getProcessMemoryInfo()? Es evidente que desea que los datos resultantes sean significativos, entonces, ¿por qué Google ha dificultado tanto la correlación de los resultados? El sistema actual ni siquiera funciona bien si quiero procesar todo el uso de la memoria, ya que el resultado devuelto es una matriz de objetos android.os.Debug.MemoryInfo, pero ninguno de esos objetos realmente le dice a qué pids están asociados. Si simplemente pasa una matriz de todos los pids, no tendrá forma de comprender los resultados. Según tengo entendido, no tiene sentido pasar más de un pid a la vez, y luego, si ese es el caso, ¿por qué hacer que ActivityManager.getProcessMemoryInfo() solo tome una matriz int?
Hackbod's es una de las mejores respuestas en Stack Overflow. Arroja luz sobre un tema muy oscuro. Me ayudó mucho.
Otro recurso realmente útil es este vídeo imprescindible: Google I/O 2011: Gestión de memoria para aplicaciones de Android.
ACTUALIZAR:
Process Stats, un servicio para descubrir cómo su aplicación administra la memoria, explicado en la publicación del blog Process Stats: Understanding How Your App Uses RAM de Dianne Hackborn:
Android Studio 0.8.10+ ha introducido una herramienta increíblemente útil llamada Memory Monitor .
Para qué sirve:
- Muestra la memoria disponible y utilizada en un gráfico y eventos de recolección de basura a lo largo del tiempo.
- Probar rápidamente si la lentitud de la aplicación podría estar relacionada con eventos excesivos de recolección de basura.
- Probar rápidamente si los fallos de la aplicación pueden estar relacionados con la falta de memoria.
Figura 1. Forzar un evento GC (recolección de basura) en Android Memory Monitor
Puede tener mucha buena información sobre el consumo de RAM de su aplicación en tiempo real al usarla.