Pregunta ¿Cómo descubro el uso de memoria de mi aplicación en Android?


¿Cómo puedo encontrar la memoria utilizada en mi aplicación Android, programáticamente?

Espero que haya una manera de hacerlo. Además, ¿cómo consigo la memoria gratuita del teléfono también?


745
2018-02-19 17:12


origen


Respuestas:


Tenga en cuenta que el uso de memoria en sistemas operativos modernos como Linux es un extremadamente área complicada y difícil de entender. De hecho, las posibilidades de que interpretes correctamente los números que obtienes son extremadamente bajas. (Prácticamente cada vez que veo los números de uso de memoria con otros ingenieros, siempre hay una larga discusión sobre lo que realmente significan, que solo da como resultado una conclusión vaga).

Nota: ahora tenemos documentación mucho más extensa sobre Administrar la memoria de tu aplicación que cubre gran parte del material aquí y está más actualizado con el estado de Android.

Lo primero es leer probablemente la última parte de este artículo que contiene una discusión sobre cómo se gestiona la memoria en Android:

Cambios en la API de servicio comenzando con Android 2.0

Ahora ActivityManager.getMemoryInfo() es nuestra API de más alto nivel para observar el uso general de la memoria. Esto está principalmente allí para ayudar a una aplicación a evaluar cuán cerca está el sistema de no tener más memoria para los procesos en segundo plano, por lo que es necesario comenzar a eliminar los procesos necesarios, como los servicios. Para aplicaciones Java puras, esto debería ser de poca utilidad, ya que el límite de almacenamiento dinámico de Java está ahí en parte para evitar que una aplicación pueda estresar el sistema hasta este punto.

En el nivel inferior, puede usar la API de depuración para obtener información sin procesar del núcleo 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 de 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, PrivateDirtyy SharedDirty... bueno, ahora comienza la diversión.

Mucha memoria en Android (y los sistemas Linux en general) se comparte en muchos procesos. Entonces, ¿cuánta memoria usa un proceso? Realmente no está clara. Añade encima de esa paginación al disco (y mucho menos intercambia lo 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.

los Pss number es una métrica que el kernel calcula que tiene en cuenta el uso compartido de memoria: básicamente, cada página de RAM en un proceso se escala por una proporción del número de otros procesos que también usan esa página. De esta forma, puedes (en teoría) sumar los pss en todos los procesos para ver la RAM total que están usando, y comparar pss entre procesos para obtener 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 memoria RAM que estará disponible para el sistema cuando ese proceso desaparezca (y probablemente se subsuma rápidamente en cachés y otros usos de este).

Eso es más o menos las API de SDK para esto. Sin embargo, hay más cosas que puedes hacer como desarrollador con tu dispositivo.

Utilizando adb, hay mucha información que puede obtener sobre el uso de la memoria de un sistema en ejecución. Uno común es el comando adb shell dumpsys meminfo que escupirá un montón de información sobre el uso de la memoria de cada proceso de Java, que contiene la información anterior, así como una variedad de otras cosas. También puede virar 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] **
                    dalvik nativo otro total
            tamaño: 10940 7047 N / A 17987
       asignado: 8943 5516 N / A 14459
            gratis: 336 1531 N / A 1867
           (Pss): 4585 9282 11916 25783
  (compartido sucio): 2184 3596 916 6696
    (Priv sucio): 4504 5956 7456 17916

 Objetos
           Vistas: 149 ViewRoots: 4
     AppContexts: 13 Actividades: 0
          Activos: 4 AssetManagers: 4
   Enlazadores locales: 141 Enlazadores de proxy: 158
Destinatarios de la muerte: 49
 Sockets de OpenSSL: 0

 SQL
            Heap: 205 dbFiles: 0
       numPagers: 0 inactivePageKB: 0
    activePageKB: 0

La sección superior es la principal, donde size es el tamaño total en el espacio de direcciones de un montón en particular, allocated es el kb de las asignaciones reales que el montón cree que tiene, free es el kb restante libre que el montón tiene para asignaciones adicionales, y pss y priv dirty son los mismos que se discutieron antes, específicos de las páginas asociadas con cada uno de los montones.

Si solo desea ver el uso de la memoria en todos los procesos, puede usar el comando adb shell procrank. La salida de esto en el mismo sistema se ve así:

  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 / depurados

Aquí el Vss y Rss las columnas son básicamente ruido (este es el espacio de direcciones directo y el uso de la memoria RAM de un proceso, donde si suma el uso de RAM entre los procesos obtiene un número ridículamente grande).

Pss es como hemos visto antes, y Uss es Priv Dirty.

Algo interesante de notar aquí: Pss y Uss son ligeramente (o más que ligeramente) diferentes de lo que vimos en meminfo. ¿Porqué es eso? Procrank bien utiliza un mecanismo kernel diferente para recopilar sus datos que meminfo lo hace, y dan resultados ligeramente diferentes. ¿Porqué es eso? Honestamente, no tengo ni idea. Creo procrank puede ser el más preciso ... pero en realidad, esto simplemente deja el punto: "toma cualquier información de memoria que obtienes con un grano de sal, a menudo un grano muy grande".

Finalmente está el comando adb shell cat /proc/meminfo eso da un resumen del uso general de la memoria del sistema. Hay una gran cantidad de datos aquí, solo los primeros números que vale la pena discutir (y los restantes entendidos por pocas personas, y mis preguntas sobre esas pocas personas a menudo dan como resultado explicaciones contradictorias):

MemTotal: 395144 kB
MemFree: 184936 kB
Buffers: 880 kB
En caché: 84104 kB
SwapCached: 0 kB

MemTotal es la cantidad total de memoria disponible para el núcleo y el espacio de usuario (a menudo menos que la RAM física real del dispositivo, ya que parte de esa RAM es necesaria para la radio, los búferes DMA, etc.).

MemFree es la cantidad de RAM que no se usa en absoluto. El número que ves aquí es muy alto; típicamente en un sistema Android esto sería solo unos pocos MB, ya que tratamos de usar la memoria disponible para mantener los procesos en ejecución

Cached es la RAM que se utiliza para cachés del sistema de archivos y otras cosas similares. Los sistemas típicos necesitarán tener 20 MB más o menos para evitar entrar en estados de paginación incorrectos; el analizador sin memoria de Android está sintonizado para un sistema particular para asegurarse de que los procesos en segundo plano se eliminen antes de que la memoria RAM almacenada en caché sea consumida demasiado por ellos como para provocar dicha paginación.


969
2018-02-19 21:44



Sí, puede obtener información de memoria mediante programación y decidir si realiza un trabajo intensivo en memoria.

Obtenga el tamaño del montón de VM llamando:

Runtime.getRuntime().totalMemory();

Obtenga la memoria de VM asignada llamando:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Obtenga el límite de tamaño de almacenamiento dinámico de VM llamando a:

Runtime.getRuntime().maxMemory()

Obtenga memoria nativa asignada llamando:

Debug.getNativeHeapAllocatedSize();

Hice una aplicación para descubrir el comportamiento OutOfMemoryError y controlar el uso de la memoria.

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

Puedes obtener el código fuente en https://github.com/coocood/oom-research


69
2017-12-01 05:49



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 ()? Claramente, desea hacer que los datos resultantes sean significativos, entonces, ¿por qué Google hizo tan difícil correlacionar 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 te dice con qué pids están asociados. Si simplemente pasa una serie de todos los pids, no tendrá forma de entender los resultados. Como entiendo su uso, no tiene sentido pasar más de un pid a la vez, y luego, si ese es el caso, ¿por qué hacerlo de modo que activityManager.getProcessMemoryInfo () solo tome una matriz int?


48
2017-08-13 18:32



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 video imprescindible: Google I / O 2011: gestión de memoria para aplicaciones de Android


ACTUALIZAR:

Process Stats, un servicio para descubrir cómo se explica tu aplicación la memoria explicada en la publicación del blog Estadísticas de proceso: entender cómo su aplicación usa RAM por Dianne Hackborn:


24
2017-12-21 16:49



Android Studio 0.8.10+ ha presentado una herramienta increíblemente útil llamada Monitor de memoria.

enter image description here

Para qué es bueno:

  • Mostrando memoria disponible y usada en un gráfico y recolección de basura   eventos a lo largo del tiempo
  • Probando rápidamente si la lentitud de la aplicación podría ser   relacionado con eventos excesivos de recolección de basura.
  • Pruebas rápidas   si los bloqueos de aplicaciones pueden estar relacionados con la falta de memoria.

enter image description here

Figura 1. Forzar un evento GC (recolección de basura) en el monitor de memoria Android

Puede tener mucha información buena sobre el consumo en tiempo real de la RAM de su aplicación al usarla.


19
2017-08-12 12:40



1) Supongo que no, al menos no de Java.
2)

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

16
2018-02-19 17:31



Descubrimos que todas las formas estándar de obtener la memoria total del proceso actual tienen algunos problemas.

  • Runtime.getRuntime().totalMemory(): solo devuelve memoria JVM
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() y cualquier otra cosa basada en /proc/meminfo - devuelve información de memoria sobre todos los procesos combinados (p. android_util_Process.cpp)
  • Debug.getNativeHeapAllocatedSize() - usos mallinfo() que devuelve información acerca de las asignaciones de memoria realizadas por malloc() y funciones relacionadas solamente (ver android_os_Debug.cpp)
  • Debug.getMemoryInfo() - hace el trabajo, pero es demasiado lento. Se tarda como 200ms en Nexus 6 para una sola llamada. La sobrecarga del rendimiento hace que esta función sea inútil para nosotros, ya que la llamamos con regularidad y cada llamada es bastante notable (ver android_os_Debug.cpp)
  • ActivityManager.getProcessMemoryInfo(int[]) - llamadas Debug.getMemoryInfo() internamente (ver ActivityManagerService.java)

Finalmente, terminamos usando el siguiente código:

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;

Vuelve VmRSS métrico. Puede encontrar más detalles al respecto aquí: uno, dos y Tres.


PD Me di cuenta de que el tema todavía tiene una falta de un fragmento de código real y simple de cómo estimar el uso de la memoria privada del proceso si el rendimiento no es un requisito crítico:

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
2017-08-14 02:02



Hay muchas respuestas arriba que definitivamente te ayudarán pero (después de 2 días de pagar e investigar sobre las herramientas de memoria adb) Creo que puedo ayudar con mi opinión también.

Como Hackbod dice: 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.   entonces no hay manera de que pueda obtener la cantidad exacta de memoria por proceso.

Pero puede acercarse a él por alguna lógica ... y le diré cómo ...

Hay algunas API como android.os.Debug.MemoryInfo y ActivityManager.getMemoryInfo() mencionado anteriormente que ya podría haber leído y utilizado, pero voy a hablar de otra manera

Entonces, primero debe ser un usuario root para que funcione. Acceda a la consola con el privilegio de root ejecutando su en proceso y obtener su output and input stream. Luego pasa id\n  (entrar) en ouputstream y escríbalo para procesar el resultado, If obtendrá un inputstream que contiene uid=0, usted es usuario root

Ahora aquí está la lógica que usarás en el proceso anterior

Cuando obtienes ouputstream del proceso pasas tu comando (procrank, dumpsys meminfo etc ...) con \n en lugar de id y obtener su inputstream y leer, almacenar la secuencia en bytes [], char [], etc. crudo datos ... ¡y terminaste!

permiso:

<uses-permission android:name="android.permission.FACTORY_TEST"/>

Verifica si eres usuario 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
   } 
}

Ejecute su comando con 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: result

Obtienes datos brutos en una sola cadena desde la consola en lugar de en cualquier instancia desde cualquier API, que es compleja de almacenar, ya que necesitarás separarla manualmente.

Esto es solo un intento, por favor sugiérame si me perdí algo


0
2018-02-20 12:37