Rileva le dimensioni dell'heap dell'applicazione in Android


Risposte:


453

Esistono due modi per pensare alla frase "dimensione heap dell'applicazione disponibile":

  1. Quanto heap può utilizzare la mia app prima che venga attivato un errore grave? E

  2. Quanto heap dovrebbe usare la mia app, dati i vincoli della versione del sistema operativo Android e dell'hardware del dispositivo dell'utente?

Esiste un metodo diverso per determinare ciascuno dei precedenti.

Per l'articolo 1 sopra: maxMemory()

che può essere invocato (ad es. nel onCreate()metodo della tua attività principale ) come segue:

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

Questo metodo indica quanti byte totali di heap è consentito utilizzare l'app .

Per l'articolo 2 sopra: getMemoryClass()

che può essere invocato come segue:

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

Questo metodo ti dice approssimativamente quanti megabyte di heap la tua app dovrebbe usare se vuole essere correttamente rispettosa dei limiti del dispositivo attuale e dei diritti di altre app da eseguire senza essere ripetutamente forzata nel onStop()/ onResume()ciclo poiché sono maleducati svuotato dalla memoria mentre l'app per elefanti fa il bagno nella vasca idromassaggio Android.

Questa distinzione non è chiaramente documentata, per quanto ne so, ma ho testato questa ipotesi su cinque diversi dispositivi Android (vedi sotto) e ho confermato con mia soddisfazione che questa è un'interpretazione corretta.

Per una versione stock di Android, maxMemory()in genere restituisce circa lo stesso numero di megabyte indicati in getMemoryClass()(ovvero circa un milione di volte il secondo valore).

L'unica situazione (di cui sono a conoscenza) per la quale i due metodi possono divergere è su un dispositivo rootato che esegue una versione Android come CyanogenMod, che consente all'utente di selezionare manualmente la dimensione dell'heap che deve essere consentita per ciascuna app. In CM, ad esempio, questa opzione appare sotto "Impostazioni CyanogenMod" / "Prestazioni" / "Dimensione heap VM".

NOTA: CONSIDERARE CHE IMPOSTARE QUESTO VALORE MANUALMENTE PU M ESSERE MESSAGGIO DEL SISTEMA, IN PARTICOLARE se si seleziona un valore inferiore a quello normale per il proprio dispositivo.

Ecco i risultati dei miei test che mostrano i valori restituiti da maxMemory()e getMemoryClass()per quattro diversi dispositivi che eseguono CyanogenMod, utilizzando due diversi valori heap (impostati manualmente) per ciascuno:

  • G1:
    • Con Dimensione heap VM impostata su 16 MB:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Con Dimensione heap VM impostata su 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Con Dimensione heap VM impostata su 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Con Dimensione heap VM impostata su 16 MB:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Con la dimensione dell'heap di VM impostata su 32 MB:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Con la dimensione dell'heap di VM impostata su 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • Con Dimensione heap VM impostata su 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Con Dimensione heap VM impostata su 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

Oltre a quanto sopra, ho testato su un tablet Novo7 Paladin con Ice Cream Sandwich. Questa era essenzialmente una versione stock di ICS, tranne per il fatto che ho effettuato il root del tablet attraverso un semplice processo che non sostituisce l'intero sistema operativo, e in particolare non fornisce un'interfaccia che consentirebbe di regolare manualmente le dimensioni dell'heap.

Per quel dispositivo, ecco i risultati:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

Inoltre (per Kishore in un commento qui sotto):

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

E (per il commento di akauppi):

  • Samsung Galaxy Core Plus
    • maxMemory: (Non specificato nel commento)
    • getMemoryClass: 48
    • largeMemoryClass: 128

Per un commento da cmcromance:

  • Grande mucchio di Galaxy S3 (Jelly Bean)
    • maxMemory: 268435456
    • getMemoryClass: 64

E (per i commenti di Tencent):

  • LG Nexus 5 (4.4.3) normale
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Heap di grandi dimensioni per LG Nexus 5 (4.4.3)
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normale
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) heap di grandi dimensioni
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normale
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Heap di grandi dimensioni per Galaxy S4 Play Store Edition (4.4.2)
    • maxMemory: 536870912
    • getMemoryClass: 192

Altri dispositivi

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

Non ho testato questi due metodi usando lo speciale android: largeHeap = opzione manifest "true" disponibile da Honeycomb, ma grazie a cmcromance e tencent abbiamo alcuni valori largeHeap di esempio, come riportato sopra.

La mia aspettativa (che sembra essere supportata dai numeri largeHeap sopra) sarebbe che questa opzione avrebbe un effetto simile all'impostazione manuale dell'heap tramite un sistema operativo rootato, cioè aumenterebbe il valore di maxMemory()quando si lascia getMemoryClass()da soli. Esiste un altro metodo, getLargeMemoryClass (), che indica la quantità di memoria consentita per un'app utilizzando l'impostazione largeHeap. La documentazione per getLargeMemoryClass () afferma che "la maggior parte delle applicazioni non dovrebbe aver bisogno di questa quantità di memoria e dovrebbe invece rimanere con il limite getMemoryClass ()."

Se ho indovinato correttamente, l'utilizzo di tale opzione avrebbe gli stessi vantaggi (e pericoli) di quello dello spazio reso disponibile da un utente che ha aumentato l'heap tramite un sistema operativo root (ovvero, se l'app utilizza la memoria aggiuntiva, probabilmente non giocherà altrettanto bene con qualsiasi altra app che l'utente stia eseguendo contemporaneamente).

Sembra che la classe di memoria apparentemente non debba essere un multiplo di 8 MB.

Possiamo vedere da quanto sopra che il getMemoryClass()risultato è invariato per una data configurazione dispositivo / sistema operativo, mentre il valore maxMemory () cambia quando l'heap è impostato in modo diverso dall'utente.

La mia esperienza pratica è che sul G1 (che ha una classe di memoria di 16), se seleziono manualmente 24 MB come dimensione dell'heap, posso eseguire senza errori anche quando il mio utilizzo della memoria può spostarsi fino a 20 MB (presumibilmente potrebbe andare fino a 24 MB, anche se non ho provato questo). Ma altre app di dimensioni altrettanto grandi potrebbero essere cancellate dalla memoria a causa della porosità della mia app. E, al contrario, la mia app potrebbe essere cancellata dalla memoria se queste altre app ad alta manutenzione vengono portate in primo piano dall'utente.

Pertanto, non è possibile andare oltre la quantità di memoria specificata da maxMemory(). E, dovresti cercare di rimanere nei limiti specificati da getMemoryClass(). Un modo per farlo, se tutto il resto fallisce, potrebbe essere quello di limitare la funzionalità di tali dispositivi in ​​modo da preservare la memoria.

Infine, se prevedi di superare il numero di megabyte specificato in getMemoryClass(), il mio consiglio sarebbe di lavorare a lungo e duramente sul salvataggio e il ripristino dello stato della tua app, in modo che l'esperienza dell'utente sia praticamente ininterrotta se si verifica un onStop()/ onResume()ciclo.

Nel mio caso, per motivi di prestazioni, sto limitando la mia app ai dispositivi con 2.2 e versioni successive e ciò significa che quasi tutti i dispositivi che eseguono la mia app avranno una MemoryClass di 24 o superiore. Quindi posso progettare di occupare fino a 20 MB di heap e sono abbastanza fiducioso che la mia app funzionerà bene con le altre app che l'utente potrebbe eseguire contemporaneamente.

Ma ci saranno sempre alcuni utenti rooted che hanno caricato una versione 2.2 o successiva di Android su un dispositivo più vecchio (ad esempio un G1). Quando ti imbatti in una tale configurazione, idealmente, dovresti limitare l'uso della memoria, anche se ti maxMemory()sta dicendo che puoi andare molto più in alto rispetto ai 16 MB che ti getMemoryClass()sta dicendo che dovresti mirare. E se non riesci ad assicurarti in modo affidabile che la tua app vivrà entro quel budget, almeno assicurati che onStop()/ onResume()funzioni senza problemi.

getMemoryClass(), come indicato da Diane Hackborn (hackbod) sopra, è disponibile solo di nuovo al livello API 5 (Android 2.0), e quindi, come lei consiglia, puoi supporre che l'hardware fisico di qualsiasi dispositivo che esegue una versione precedente del sistema operativo sia progettato per supportare in modo ottimale le app che occupano uno spazio heap non superiore a 16 MB.

Al contrario, maxMemory()secondo la documentazione, è disponibile tutto il viaggio di ritorno a livello di API 1. maxMemory(), su una versione pre-2.0, sarà probabilmente restituire un valore da 16 MB, ma io faccio vedere che nel mio (molto più tardi) le versioni CyanogenMod l'utente può selezionare un valore di heap di soli 12 MB, il che presumibilmente comporterebbe un limite di heap più basso e quindi suggerirei di continuare a testare il maxMemory()valore, anche per le versioni del sistema operativo precedenti alla 2.0. Potrebbe anche essere necessario rifiutare l'esecuzione nel caso improbabile che questo valore sia impostato anche inferiore a 16 MB, se è necessario disporre di più di quanto maxMemory()indicato è consentito.


2
@Carl Ciao Carl, grazie per il tuo fantastico post. E ho testato l'opzione, android: largeHeap = "true" su Galaxy S3 con JellyBean. Ecco il risultato. [maxMemory: 256,0 MB, memoryClass: 64 MB] (maxMemory era 64 MB senza l'opzione largeheap)
cmcromance

1
@Carl Haha È un grande onore! :)
cmcromance

1
Per HTC One X: max Memoria: 67108864 memoryClass: 64
Kishore

1
LG Nexus 5 (4.4.3) (normale): max Memoria: 201326592, memoria Classe: 192 | LG Nexus 5 (4.4.3) (heap di grandi dimensioni): max Memoria: 536870912, memoria Classe: 192
ian.shaun.thomas

1
Molto ben documentato. Si prega di fornire questo ad Android. Non riesco a trovare un documento Android così appropriato
Jimit Patel,

20

L' API ufficiale è:

Questo è stato introdotto nel 2.0 dove apparivano dispositivi di memoria più grandi. Si può presumere che i dispositivi che eseguono versioni precedenti del sistema operativo utilizzino la classe di memoria originale (16).


13

Debug.getNativeHeapSize()farà il trucco, dovrei pensare. È stato lì dal 1.0, però.

La Debugclasse ha molti ottimi metodi per tenere traccia delle allocazioni e altri problemi di prestazioni. Inoltre, se è necessario rilevare una situazione di memoria insufficiente, controllare Activity.onLowMemory().


Grazie Neil. Funziona quando l'applicazione è in esecuzione con il debug disattivato?
hpique

2
Sono abbastanza nuovo per questo, ma non penso che l'heap nativo sia lo stesso dell'heap dell'app (Dalvik) a cui si riferisce la domanda. L'heap nativo fornisce il supporto per i dati bitmap, che sono allocati dal codice nativo, mentre l'heap dell'app contiene i dati dell'applicazione Java. Ero interessato a sapere che l'heap nativo viene conteggiato rispetto al limite dell'heap dell'app e dopo 3.0 le allocazioni si verificano effettivamente sull'heap dell'app. Diane Hackborn (hackbod) pubblica qui questo argomento: stackoverflow.com/questions/1945142/bitmaps-in-android Ma anche per 3.0, ci sono anche dati non "nativi" sull'heap dell'app.
Carl,

1
Da qualche recente aggiornamento di Android (forse KitKat 4.4) le bitmap sono nell'heap JVM.
akauppi,

13

Ecco come lo fai:

Ottenere la dimensione heap massima che l'app può usare:

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

Ottenere la quantità di heap attualmente utilizzata dalla tua app:

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

Ottenere la quantità di heap che la tua app può ora utilizzare (memoria disponibile):

long availableMemory=maxMemory-usedMemory;

E, per formattare ciascuno di essi in modo gradevole, puoi usare:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 

5

Ciò restituisce la dimensione massima dell'heap in byte:

Runtime.getRuntime().maxMemory()

Stavo usando ActivityManager.getMemoryClass () ma su CyanogenMod 7 (non l'ho provato altrove) restituisce un valore errato se l'utente imposta manualmente la dimensione dell'heap.


Dal momento che il documento per getMemoryClasssembra implicare che il numero potrebbe non essere uguale alla dimensione dell'heap disponibile per la tua VM, e poiché il documento per getNativeHeapSizeè ... taciturno, penso davvero che Runtime.getRuntime().maxMemory()sia la risposta migliore.
CdA

3

Alcune operazioni sono più veloci di java heap space manager. Ritardare le operazioni per qualche tempo può liberare spazio di memoria. È possibile utilizzare questo metodo per evitare l'errore di dimensione heap:

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();
  }
}

testato bene. Puoi elaborare queste due cose: availableMemoryPercentage e MIN_AVAILABLE_MEMORY_PERCENTAGE? alcune spiegazioni?
Noor Hossain,

1
AvailableMemoryPercentageè secondo la formula: quanta memoria del dispositivo è attualmente libera. MIN_AVAILABLE_MEMORY_PERCENTAGEè il tuo parametro personalizzato, una soglia alla quale inizi ad aspettare che Garbage Collector faccia il suo lavoro.
Zon

1

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

Ho fatto l'errore di prototipare il mio gioco sul Nexus 7 e poi scoprirlo è rimasto quasi esaurito quasi immediatamente sulla memoria del tablet generico 4.04 di mia moglie (memoryclass 48, maxmemory 50331648)

Dovrò ristrutturare il mio progetto per caricare meno risorse quando determino che la classe di memoria è bassa.
C'è un modo in Java per vedere l'attuale dimensione dell'heap? (Riesco a vederlo chiaramente in logCat durante il debug, ma mi piacerebbe un modo per vederlo nel codice per adattarlo, come se currentheap> (maxmemory / 2) scaricasse bitmap di alta qualità caricasse di bassa qualità


Su detto Nexus7-2013, getLargeMemoryClass () = 512.
akauppi

0

Intendi a livello di programmazione o solo mentre stai sviluppando e eseguendo il debug? In quest'ultimo caso, è possibile visualizzare tali informazioni dalla prospettiva DDMS in Eclipse. Quando il tuo emulatore (possibilmente anche un telefono fisico collegato) è in esecuzione, elencherà i processi attivi in ​​una finestra a sinistra. Puoi selezionarlo e c'è un'opzione per tenere traccia delle allocazioni di heap.


Intendo a livello di programmazione. Chiarita la domanda. Grazie.
hpique

1
Ti suggerisco di guardare questo post da uno dei programmatori della piattaforma Android quindi: stackoverflow.com/questions/2298208/…
Steve Haley

0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

il valore è b

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

il valore è MB


Che tipo di rt? Dove viene dichiarato?
FindOut_Quran,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.