Qual è la quantità massima di RAM che un'app può usare?


145

Sono abbastanza curioso di questa domanda relativa alla gestione della memoria del sistema operativo Android, quindi spero in una risposta abbastanza dettagliata su questo argomento.

Cosa vorrei sapere:

  • Qual è la quantità massima di memoria (in megabyte / in percentuale della RAM totale) che può essere utilizzata da un'applicazione Android (che non è un'app di sistema)?
  • Ci sono differenze tra le versioni di Android ?
  • Ci sono differenze relative al produttore del dispositivo?

E, soprattutto:

  • Cosa viene considerato / da cosa dipende quando si tratta del sistema che determina la quantità di RAM che un'app può utilizzare in fase di esecuzione (supponendo che il massimo di memoria per app non sia un numero statico)?

Quello che ho sentito finora (fino al 2013):

  • I primi dispositivi Android avevano un limite per app di 16 MB
  • Successivamente questo limite è aumentato a 24 MB o 32 MB

Cosa mi rende molto curioso:

Entrambi questi limiti sono molto bassi.

Ho appena scaricato Android Task Manager per controllare la RAM dei miei dispositivi. Quello che ho notato è che ci sono applicazioni che usano circa 40-50 megabyte di RAM, il che è ovvio più dell'utilizzo massimo di RAM menzionato, diciamo 32 MB. Quindi, in che modo Android determina quanta RAM può utilizzare un'app? Come è possibile che le app superino quel limite?

Inoltre, ho notato che alcune app del mio si bloccano (uccise dal sistema?) Con una OutOfMemoryException quando si usano circa 30-40 Megabyte. D'altro canto, sul mio telefono sono in esecuzione app che utilizzano 100 MB e oltre dopo qualche tempo (probabilmente a causa di perdite di memoria) che non si arrestano in modo anomalo o vengono uccise. Quindi ovviamente dipende anche dall'app stessa quando si tratta di determinare quanta RAM può essere risparmiata. Com'è possibile? (Ho condotto i miei test con un HTC One S con 768 MB di RAM)

Dichiarazione di non responsabilità: NON sono affiliato all'app Android Task Manager in alcun modo.

Risposte:


119

Qual è la quantità massima di memoria (in Megabyte / come percentuale della RAM totale) che può essere utilizzata da un'applicazione Android (che non è un'app di sistema)?

Ciò varia in base al dispositivo. getMemoryClass()onActivityManager ti darà il valore per il dispositivo su cui è in esecuzione il tuo codice.

Ci sono differenze tra le versioni di Android?

Sì, in quanto i requisiti del sistema operativo sono aumentati nel corso degli anni e i dispositivi devono adattarsi per adattarsi.

Ci sono differenze relative al produttore del dispositivo?

Sì, nella misura in cui i produttori producono dispositivi e le dimensioni variano in base al dispositivo.

Quali "fattori collaterali" sono presi in considerazione quando si tratta di determinare quanta RAM può usare un'app?

Non ho idea di cosa significhino "fattori collaterali".

I primi dispositivi avevano un limite per app di 16 MB; I dispositivi successivi lo hanno aumentato a 24 MB o 32 MB

È giusto. La risoluzione dello schermo è un fattore determinante, poiché risoluzioni più grandi significano bitmap più grandi, quindi tablet e telefoni ad alta risoluzione tenderanno ad avere valori ancora più alti. Ad esempio, vedrai dispositivi con heap da 48 MB e non sarei sorpreso se ci sono valori più alti di così.

Come è possibile che le app superino quel limite?

Supponi che l'autore di quell'app sappia cosa sta facendo. Considerando che l'utilizzo della memoria di un'app è difficile da determinare per un ingegnere Android di base , non presumo che l'app in questione fornisca necessariamente risultati particolarmente accurati.

Detto questo, il codice nativo (NDK) non è soggetto al limite di heap. E, da Android 3.0, le app possono richiedere un "heap di grandi dimensioni", in genere nell'intervallo di centinaia di MB, ma è considerato una forma scadente per la maggior parte delle app.

Inoltre, ho notato che alcune mie app si bloccano con OutOfMemoryException quando si usano circa 30-40 Megabyte.

Ricorda che il Garbage Collector Android non è un Garbage Collector compatto. L'eccezione dovrebbe essere davvero CouldNotFindSufficientlyLargeBlockOfMemoryException, ma probabilmente è stata considerata troppo prolissa. OutOfMemoryExceptionsignifica che non è stato possibile allocare il blocco richiesto , non che si è esaurito completamente l'heap.


Non riesco a capire il tavolo, ho Xperia X mobile che ha una risoluzione di circa 1080 x 1920 che è una grande risoluzione, e un altro dispositivo Samsung Tab 4 che la risoluzione è 800 x 1280, quindi ha la stessa occupazione di RAM, per favore guidami perché il cellulare viene fornito con 3 GB di RAM e scheda vengono forniti con 1,5 GB di RAM, quindi il tablet occupa una grande ram a causa del grande schermo?
Rahul Mandaliya,

@RahulMandaliya: mi dispiace, ma non capisco la tua preoccupazione o cosa abbia a che fare con questa domanda. Potresti voler aprire una domanda Stack Overflow separata in cui spieghi in dettaglio qual è la tua preoccupazione.
Commons War

15

È la fine del 2018, quindi le cose sono cambiate.

Prima di tutto: esegui la tua app e apri la scheda Profiler Android in Android Studio. Vedrai quanta memoria consuma, rimarrai sorpreso ma può allocare molta RAM.

Anche qui c'è un ottimo articolo in documenti ufficiali con istruzioni dettagliate su come usare Memory Profiler che può darti uno sguardo approfondito sulla tua gestione della memoria.

Ma nella maggior parte dei casi, il tuo normale profilo Android sarà sufficiente per te.

inserisci qui la descrizione dell'immagine

Di solito, un'app inizia con 50 MB di allocazione RAM ma salta all'istante fino a 90 Mb quando inizi a caricare alcune foto in memoria. Quando apri Activity con un ViewPager con foto precaricate (3,5 Mb ciascuna) puoi ottenere 190 Mb facilmente in pochi secondi.

Ma questo non significa che hai problemi con la gestione della memoria.

Il miglior consiglio che posso dare è seguire le linee guida e le migliori pratiche, usare le migliori librerie per il caricamento delle immagini (Glide, Picasso) e starai bene.


Ma se hai bisogno di personalizzare qualcosa e hai davvero bisogno di sapere quanta memoria puoi allocare manualmente puoi ottenere la memoria libera totale e calcolare una porzione predeterminata (in%) da essa. Nel mio caso, dovevo memorizzare nella cache le foto decodificate in modo da non doverle decrittografare ogni volta che l'utente scorre l'elenco.

A tale scopo è possibile utilizzare la classe LruCache pronta per l'uso . È una classe di cache che traccia automaticamente la quantità di memoria allocata dagli oggetti (o il numero di istanze) e rimuove la più vecchia per conservare la recente dalla cronologia di utilizzo. Ecco un ottimo tutorial su come usarlo.

Nel mio caso, ho creato 2 istanze di cache: per pollici e allegati. Li ha resi statici con accesso singleton in modo che siano disponibili a livello globale in tutta l'app.

classe di cache:

public class BitmapLruCache extends LruCache<Uri, byte[]> {

    private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f; // 1% (Nexus 5X - 5Mb)
    private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;// 3% (Nexus 5X - 16Mb)
    private static BitmapLruCache thumbCacheInstance;
    private static BitmapLruCache attachmentCacheInstance;

public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() {
    if (thumbCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC);
    //L.log("creating BitmapLruCache for Thumb with size: " + cacheSize + " bytes");
        thumbCacheInstance = new BitmapLruCache(cacheSize);
        return thumbCacheInstance;
    } else {
        return thumbCacheInstance;
    }
}

public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() {
    if (attachmentCacheInstance == null) {

        int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC);
    //            L.log("creating BitmapLruCache for Attachment with size: " + cacheSize + " bytes");
        attachmentCacheInstance = new BitmapLruCache(cacheSize);
        return attachmentCacheInstance;
    } else {
        return attachmentCacheInstance;
    }
}

private BitmapLruCache(int maxSize) {
    super(maxSize);
}

public void addBitmap(Uri uri, byte[] bitmapBytes) {
    if (get(uri) == null && bitmapBytes != null)
        put(uri, bitmapBytes);
}

public byte[] getBitmap(Uri uri) {
    return get(uri);
}


@Override
protected int sizeOf(Uri uri, byte[] bitmapBytes) {
    // The cache size will be measured in bytes rather than number of items.
    return bitmapBytes.length;
}
}

Ecco come calcolo la RAM libera disponibile e quanto posso mordere:

private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){
    final long maxMemory = Runtime.getRuntime().maxMemory();
    //Use ... of available memory for List Notes thumb cache
    return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache);
}

Ed è così che lo uso negli adattatori per ottenere l'immagine memorizzata nella cache:

byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);

e come l'ho impostato nella cache in thread in background (AsyncTask normale):

BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes); 

La mia app ha come target API 19+, quindi i dispositivi non sono vecchi e queste porzioni di RAM disponibili sono abbastanza buone per la cache nel mio caso (1% e 3%).

Curiosità: Android non ha API o altri hack per ottenere la quantità di memoria allocata alla tua app, viene calcolata al volo in base a vari fattori.


PS Sto usando un campo di classe statica per contenere una cache, ma secondo le più recenti linee guida di Android è consigliato utilizzare il componente di architettura ViewModel a tale scopo.


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.