Detecta el tamaño del montón de aplicaciones en Android

145

¿Cómo detecta mediante programación el tamaño de almacenamiento dinámico de la aplicación disponible para una aplicación de Android?

Escuché que hay una función que hace esto en versiones posteriores del SDK. En cualquier caso, estoy buscando una solución que funcione para 1.5 y más.

hpique
fuente
2
Relacionado: stackoverflow.com/questions/1518154/…
Josh Lee

Respuestas:

453

Hay dos formas de pensar en su frase "tamaño de almacenamiento dinámico de aplicación disponible":

  1. ¿Cuánto montón puede usar mi aplicación antes de que se active un error grave? Y

  2. ¿Cuánto montón debería usar mi aplicación, dadas las limitaciones de la versión del sistema operativo Android y el hardware del dispositivo del usuario?

Hay un método diferente para determinar cada uno de los anteriores.

Para el artículo 1 anterior: maxMemory()

que se puede invocar (por ejemplo, en el onCreate()método de su actividad principal ) de la siguiente manera:

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

Este método le indica cuántos bytes totales de almacenamiento dinámico puede usar su aplicación .

Para el artículo 2 anterior: getMemoryClass()

que se puede invocar de la siguiente manera:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

Este método le indica aproximadamente cuántos megabytes de almacenamiento dinámico debe usar su aplicación si quiere respetar adecuadamente los límites del dispositivo actual y los derechos de otras aplicaciones para ejecutarse sin ser forzados repetidamente al ciclo onStop()/ onResume()ya que son groseramente se quedó sin memoria mientras su aplicación de elefante se bañaba en el jacuzzi de Android.

Esta distinción no está claramente documentada, hasta donde yo sé, pero he probado esta hipótesis en cinco dispositivos Android diferentes (ver más abajo) y he confirmado para mi propia satisfacción que esta es una interpretación correcta.

Para una versión estándar de Android, maxMemory()generalmente devolverá aproximadamente la misma cantidad de megabytes que se indican getMemoryClass()(es decir, aproximadamente un millón de veces el último valor).

La única situación (de la que tengo conocimiento) en la que los dos métodos pueden divergir es en un dispositivo rooteado que ejecuta una versión de Android como CyanogenMod, que permite al usuario seleccionar manualmente qué tan grande debe permitirse un tamaño de almacenamiento dinámico para cada aplicación. En CM, por ejemplo, esta opción aparece en "Configuración de CyanogenMod" / "Rendimiento" / "Tamaño de almacenamiento dinámico de VM".

NOTA: TENGA EN CUENTA QUE LA CONFIGURACIÓN DE ESTE VALOR MANUALMENTE PUEDE MENSAJAR SU SISTEMA, ESPECIALMENTE si selecciona un valor más pequeño de lo normal para su dispositivo.

Aquí están los resultados de mi prueba que muestran los valores devueltos por maxMemory()y getMemoryClass()para cuatro dispositivos diferentes que ejecutan CyanogenMod, utilizando dos valores de almacenamiento dinámico diferentes (configurados manualmente) para cada uno:

  • G1:
    • Con VM Heap Size establecido en 16MB:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Con VM Heap Size establecido en 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Con VM Heap Size establecido en 24MB:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Con VM Heap Size establecido en 16MB:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Con el tamaño de VM Heap establecido en 32 MB:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Con el tamaño de VM Heap establecido en 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • Con VM Heap Size establecido en 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Con VM Heap Size establecido en 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

Además de lo anterior, probé en una tableta Novo7 Paladin con Ice Cream Sandwich. Esta era esencialmente una versión estándar de ICS, excepto que he rooteado la tableta a través de un proceso simple que no reemplaza todo el sistema operativo, y en particular no proporciona una interfaz que permita que el tamaño del montón se ajuste manualmente.

Para ese dispositivo, aquí están los resultados:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

También (por Kishore en un comentario a continuación):

  • HTC uno X
    • maxMemory: 67108864
    • getMemoryClass: 64

Y (según el comentario de akauppi):

  • Samsung Galaxy Core Plus
    • maxMemory: (no especificado en el comentario)
    • getMemoryClass: 48
    • largeMemoryClass: 128

Por un comentario de cmcromance:

  • Galaxy S3 (Jelly Bean) montón grande
    • maxMemory: 268435456
    • getMemoryClass: 64

Y (según los comentarios del tencent):

  • LG Nexus 5 (4.4.3) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) montón grande
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normal
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) montón grande
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normal
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) montón grande
    • maxMemory: 536870912
    • getMemoryClass: 192

Otros dispositivos

  • Huawei Nexus 6P (6.0.1) normal
    • maxMemory: 201326592
    • getMemoryClass: 192

No he probado estos dos métodos con la opción de manifiesto especial android: largeHeap = "true" disponible desde Honeycomb, pero gracias a cmcromance y tencent tenemos algunos valores de muestra de largeHeap, como se informó anteriormente.

Mi expectativa (que parece estar respaldada por los números grandes de Heap anteriores) sería que esta opción tendría un efecto similar a la configuración manual del montón a través de un sistema operativo rooteado, es decir, aumentaría el valor de maxMemory()dejarlo getMemoryClass()solo. Hay otro método, getLargeMemoryClass (), que indica la cantidad de memoria permitida para una aplicación que usa la configuración largeHeap. La documentación de getLargeMemoryClass () establece que "la mayoría de las aplicaciones no deberían necesitar esta cantidad de memoria y, en cambio, deberían permanecer con el límite getMemoryClass ()".

Si lo he adivinado correctamente, usar esa opción tendría los mismos beneficios (y peligros) que usaría el espacio disponible por un usuario que ha aumentado el montón a través de un sistema operativo rooteado (es decir, si su aplicación usa la memoria adicional, probablemente no jugará tan bien con cualquier otra aplicación que el usuario esté ejecutando al mismo tiempo).

Tenga en cuenta que la clase de memoria aparentemente no necesita ser un múltiplo de 8 MB.

Podemos ver de lo anterior que el getMemoryClass()resultado no cambia para una configuración de dispositivo / SO dada, mientras que el valor maxMemory () cambia cuando el usuario configura el montón de manera diferente.

Mi propia experiencia práctica es que en el G1 (que tiene una clase de memoria de 16), si selecciono manualmente 24 MB como tamaño de almacenamiento dinámico, puedo ejecutarlo sin errores incluso cuando mi uso de memoria puede aumentar a 20 MB (presumiblemente podría ir tan alto como 24 MB, aunque no he probado esto). Pero otras aplicaciones similares de gran tamaño pueden ser borradas de la memoria como resultado de la fragilidad de mi propia aplicación. Y, por el contrario, mi aplicación puede enjuagarse de la memoria si el usuario pone en primer plano estas otras aplicaciones de alto mantenimiento.

Por lo tanto, no puede superar la cantidad de memoria especificada por maxMemory(). Y, debe intentar mantenerse dentro de los límites especificados por getMemoryClass(). Una forma de hacerlo, si todo lo demás falla, podría ser limitar la funcionalidad de dichos dispositivos de manera que conserve la memoria.

Finalmente, si planea superar el número de megabytes especificado getMemoryClass(), mi consejo sería trabajar mucho y duro para guardar y restaurar el estado de su aplicación, de modo que la experiencia del usuario sea prácticamente ininterrumpida si ocurre un onStop()/ onResume()ciclo.

En mi caso, por razones de rendimiento, estoy limitando mi aplicación a dispositivos que ejecutan 2.2 y superiores, y eso significa que casi todos los dispositivos que ejecutan mi aplicación tendrán una clase de memoria de 24 o superior. Por lo tanto, puedo diseñar para ocupar hasta 20 MB de almacenamiento dinámico y sentirme bastante seguro de que mi aplicación funcionará bien con las otras aplicaciones que el usuario puede estar ejecutando al mismo tiempo.

Pero siempre habrá algunos usuarios rooteados que hayan cargado una versión 2.2 o superior de Android en un dispositivo anterior (por ejemplo, un G1). Cuando encuentre una configuración de este tipo, idealmente, debería reducir el uso de su memoria, incluso si maxMemory()le dice que puede ir mucho más allá de los 16 MB que getMemoryClass()le indican que debe apuntar. Y si no se puede garantizar de manera fiable que su aplicación va a vivir dentro de ese presupuesto, entonces por lo menos asegurarse de que onStop()/ onResume()funciona a la perfección.

getMemoryClass(), según lo indicado anteriormente por Diane Hackborn (hackbod), solo está disponible para volver al nivel 5 de API (Android 2.0), por lo que, como aconseja, puede asumir que el hardware físico de cualquier dispositivo que ejecute una versión anterior del sistema operativo está diseñado para soportar de manera óptima aplicaciones que ocupan un espacio dinámico de no más de 16 MB.

Por el contrario, maxMemory()según la documentación, está disponible toda la parte trasera manera de nivel 1. API maxMemory(), en una versión pre-2.0, probablemente devolver un valor de 16 MB, pero yo hago ver que en mis (mucho más tarde) versiones de CyanogenMod del usuario puede seleccionar un valor de almacenamiento dinámico tan bajo como 12 MB, lo que presumiblemente daría como resultado un límite de almacenamiento dinámico más bajo, por lo que le sugiero que continúe probando el maxMemory()valor, incluso para versiones del sistema operativo anteriores a 2.0. Es posible que incluso deba negarse a ejecutar en el improbable caso de que este valor se establezca incluso por debajo de 16 MB, si necesita tener más de lo que se maxMemory()indica.

Carl
fuente
2
@ Carl Hola Carl, gracias por tu gran publicación. Y probé la opción, android: largeHeap = "true" en Galaxy S3 con JellyBean. Aquí está el resultado. [maxMemory: 256.0MB, memoryClass: 64MB] (maxMemory era 64MB sin la opción largeheap)
cmcromance
1
@ Carl Haha ¡Es un gran honor! :)
cmcromance
1
Para HTC One X: max Memoria: 67108864 memoria Clase: 64
Kishore
1
LG Nexus 5 (4.4.3) (normal): maxMemory: 201326592, memoryClass: 192 | LG Nexus 5 (4.4.3) (montón grande): maxMemory: 536870912, memoryClass: 192
ian.shaun.thomas
1
Muy bellamente documentado. Proporcione esto a Android. No puedo encontrar un documento de Android tan apropiado
Jimit Patel el
20

La API oficial es:

Esto se introdujo en 2.0 donde aparecieron dispositivos de memoria más grandes. Puede suponer que los dispositivos que ejecutan versiones anteriores del sistema operativo están utilizando la clase de memoria original (16).

hackbod
fuente
13

Debug.getNativeHeapSize()hará el truco, debería pensar. Sin embargo, ha estado allí desde 1.0.

La Debugclase tiene muchos métodos excelentes para rastrear asignaciones y otros problemas de rendimiento. Además, si necesita detectar una situación de poca memoria, consulte Activity.onLowMemory().

Neil Traft
fuente
Gracias Neil ¿Funciona esto cuando la aplicación se ejecuta con la depuración desactivada?
hpique
2
Soy bastante nuevo en esto, pero no creo que el montón nativo sea el mismo que el montón de la aplicación (Dalvik) al que se refiere la pregunta. El montón nativo proporciona respaldo para los datos de mapa de bits, que se asigna por código nativo, mientras que el montón de la aplicación contiene datos de la aplicación Java. Me interesó saber que el montón nativo se cuenta contra el límite del montón de la aplicación, y después de 3.0 las asignaciones realmente se producen en el montón de la aplicación. Diane Hackborn (hackbod) publica sobre este tema aquí: stackoverflow.com/questions/1945142/bitmaps-in-android Pero incluso para 3.0, también hay datos no "nativos" en el montón de aplicaciones.
Carl
1
Desde alguna actualización reciente de Android (quizás KitKat 4.4), los mapas de bits están en el montón de JVM.
akauppi
13

Así es como lo haces:

Obtener el tamaño máximo de almacenamiento dinámico que la aplicación puede usar:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

Obteniendo cuánto del montón utiliza actualmente su aplicación:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

Obtener cuánto del montón puede usar ahora su aplicación (memoria disponible):

long availableMemory=maxMemory-usedMemory;

Y, para formatear bien cada uno de ellos, puede usar:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 
desarrollador de Android
fuente
5

Esto devuelve el tamaño máximo de almacenamiento dinámico en bytes:

Runtime.getRuntime().maxMemory()

Estaba usando ActivityManager.getMemoryClass () pero en CyanogenMod 7 (no lo probé en otro lugar) devuelve un valor incorrecto si el usuario establece el tamaño del montón manualmente.

fhucho
fuente
Dado que el documento para getMemoryClassparece implicar que el número puede no ser el mismo que el tamaño de almacenamiento dinámico disponible para su vm, y dado que el documento para getNativeHeapSizees ... taciturno, realmente creo que Runtime.getRuntime().maxMemory()es la mejor respuesta.
BoD
3

Algunas operaciones son más rápidas que Java Heap Space Manager. Retrasar las operaciones por algún tiempo puede liberar espacio en la memoria. Puede usar este método para escapar del error de tamaño de almacenamiento dinámico:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * space.
 *
 * @param callback Callback operations to be done when memory is free.
 */
public static void waitForGarbageCollector(final Runnable callback) {

  Runtime runtime;
  long maxMemory;
  long usedMemory;
  double availableMemoryPercentage = 1.0;
  final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
  final int DELAY_TIME = 5 * 1000;

  runtime =
    Runtime.getRuntime();

  maxMemory =
    runtime.maxMemory();

  usedMemory =
    runtime.totalMemory() -
    runtime.freeMemory();

  availableMemoryPercentage =
    1 -
    (double) usedMemory /
    maxMemory;

  if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {

    try {
      Thread.sleep(DELAY_TIME);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    waitForGarbageCollector(
      callback);
  } else {

    // Memory resources are availavle, go to next operation:

    callback.run();
  }
}
Zon
fuente
probado bien. ¿Puedes elaborar estas dos cosas: disponibleMemoryPercentage y MIN_AVAILABLE_MEMORY_PERCENTAGE? algunas explicaciones?
Noor Hossain
1
AvailableMemoryPercentageestá de acuerdo con la fórmula: qué cantidad de memoria del dispositivo está actualmente libre. MIN_AVAILABLE_MEMORY_PERCENTAGEes su parámetro personalizado, un umbral en el que comienza a esperar que el recolector de basura haga su trabajo.
Zon
1

Asus Nexus 7 (2013) 32 Concierto: getMemoryClass () = 192 maxMemory () = 201326592

Cometí el error de crear un prototipo de mi juego en el Nexus 7, y luego descubrí que se quedó sin memoria casi de inmediato en la tableta genérica 4.04 de mi esposa (memoryclass 48, maxmemory 50331648)

Tendré que reestructurar mi proyecto para cargar menos recursos cuando determine que la clase de memoria es baja.
¿Hay alguna manera en Java para ver el tamaño actual del montón? (Puedo verlo claramente en logCat al depurar, pero me gustaría una forma de verlo en el código para adaptarlo, como si currentheap> (maxmemory / 2) descargue mapas de bits de alta calidad cargue baja calidad

Tkraindesigns
fuente
En dicho Nexus7-2013, getLargeMemoryClass () = 512.
akauppi
0

¿Te refieres programáticamente, o solo mientras estás desarrollando y depurando? Si es lo último, puede ver esa información desde la perspectiva DDMS en Eclipse. Cuando su emulador (posiblemente incluso el teléfono físico que está enchufado) se esté ejecutando, enumerará los procesos activos en una ventana a la izquierda. Puede seleccionarlo y hay una opción para rastrear las asignaciones de almacenamiento dinámico.

Steve Haley
fuente
Quiero decir programáticamente. Aclaró la pregunta. Gracias.
hpique
1
Le sugiero que lea esta publicación de uno de los programadores de la plataforma Android y luego: stackoverflow.com/questions/2298208/…
Steve Haley
0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

el valor es b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

el valor es MB

qinqie
fuente
¿Qué tipo de rt? ¿Dónde se declara?
FindOut_Quran