Jaka jest maksymalna ilość pamięci RAM, której może używać aplikacja?

145

Jestem dość ciekawy tego pytania dotyczącego zarządzania pamięcią w systemie operacyjnym Android, więc liczę na dość szczegółową odpowiedź na ten temat.

Co chciałbym wiedzieć:

  • Jaka jest maksymalna ilość pamięci (w megabajtach / jako procent całkowitej pamięci RAM), z której może korzystać aplikacja na Androida (która nie jest aplikacją systemową)?
  • Czy są jakieś różnice między wersjami Androida ?
  • Czy są jakieś różnice dotyczące producenta urządzenia?

I co najważniejsze:

  • Co jest brane pod uwagę / od czego to zależy, jeśli chodzi o system określający, ile pamięci RAM aplikacja może używać w czasie wykonywania (zakładając, że maksymalna pamięć na aplikację nie jest liczbą statyczną)?

Co słyszałem do tej pory (do 2013 roku):

  • Wczesne urządzenia z Androidem miały limit 16 MB na aplikację
  • Później ten limit został zwiększony do 24 MB lub 32 MB

Co mnie bardzo zaciekawia:

Obie te limity są bardzo niskie.

Niedawno pobrałem Menedżera zadań Androida, aby sprawdzić pamięć RAM moich urządzeń. Zauważyłem, że niektóre aplikacje używają około 40-50 megabajtów pamięci RAM, czyli oczywiście więcej niż wspomniane maksymalne użycie pamięci RAM, powiedzmy 32 MB. Jak więc Android ustala, ile pamięci RAM może używać aplikacja? Jak to możliwe, że aplikacje przekraczają ten limit?

Co więcej, zauważyłem, że niektóre moje aplikacje ulegają awarii (zabite przez system?) Z OutOfMemoryException podczas używania około 30-40 megabajtów. Z drugiej strony mam aplikacje działające na moim telefonie, które używają 100 MB i więcej po pewnym czasie (prawdopodobnie z powodu wycieków pamięci), które nie ulegają awarii ani nie zostają zabite. Więc oczywiście zależy to również od samej aplikacji, jeśli chodzi o określenie, ile pamięci RAM można zaoszczędzić. Jak to jest możliwe? (Testy przeprowadziłem z HTC One S z 768 MB RAM)

Zastrzeżenie: NIE jestem w żaden sposób powiązany z aplikacją Android Task Manager.

Philipp Jahoda
źródło

Odpowiedzi:

119

Jaka jest maksymalna ilość pamięci (w megabajtach / jako procent całkowitej pamięci RAM), z której może korzystać aplikacja na Androida (która nie jest aplikacją systemową)?

To zależy od urządzenia. getMemoryClass()onActivityManager poda wartość urządzenia, na którym działa kod.

Czy są jakieś różnice między wersjami Androida?

Tak, o ile wymagania systemu operacyjnego wzrosły z biegiem lat, a urządzenia muszą się do nich dostosować.

Czy są różnice dotyczące producenta urządzenia?

Tak, o ile producenci wytwarzają urządzenia, a ich rozmiar różni się w zależności od urządzenia.

Jakie „czynniki uboczne” są brane pod uwagę przy określaniu ilości pamięci RAM, której może używać aplikacja?

Nie mam pojęcia, co oznaczają „czynniki uboczne”.

Wczesne urządzenia miały limit 16 MB na aplikację; Późniejsze urządzenia zwiększyły to do 24 MB lub 32 MB

Zgadza się. Rozdzielczość ekranu jest istotnym wyznacznikiem, ponieważ większe rozdzielczości oznaczają większe mapy bitowe, dlatego tablety i telefony o wysokiej rozdzielczości będą miały jeszcze wyższe wartości. Na przykład zobaczysz urządzenia ze stertami 48 MB i nie zdziwiłbym się, gdyby były wartości wyższe niż to.

Jak to możliwe, że aplikacje przekraczają ten limit?

Zakładasz, że autor tej aplikacji wie, co robi. Biorąc pod uwagę, że wykorzystanie pamięci przez aplikację jest trudne do określenia przez głównego inżyniera Androida , nie zakładałbym, że dana aplikacja zapewnia szczególnie dokładne wyniki.

To powiedziawszy, kod natywny (NDK) nie podlega limitowi sterty. A od Androida 3.0 aplikacje mogą żądać „dużego stosu”, zwykle w zakresie setek MB, ale w przypadku większości aplikacji jest to uważane za kiepską formę.

Ponadto zauważyłem, że niektóre moje aplikacje ulegają awarii z wyjątkiem OutOfMemoryException przy użyciu około 30-40 megabajtów.

Należy pamiętać, że moduł odśmiecania systemu Android nie jest urządzeniem do usuwania śmieci z kompaktowaniem. Naprawdę powinien być wyjątek CouldNotFindSufficientlyLargeBlockOfMemoryException, ale prawdopodobnie uznano to za zbyt rozwlekłe. OutOfMemoryExceptionoznacza, że ​​nie mogłeś przydzielić żądanego bloku , a nie że całkowicie wyczerpałeś swoją stertę.

CommonsWare
źródło
Nie rozumiem tabeli, mam telefon komórkowy Xperię X, który ma rozdzielczość około 1080 x 1920, czyli dużą rozdzielczość, i inne urządzenie Samsung Tab 4 o rozdzielczości 800 x 1280, więc zajmuje to samo zajęcie pamięci RAM, poprowadź mnie, ponieważ telefon jest wyposażony w 3 GB pamięci RAM i karta są dostarczane z 1,5 GB pamięci RAM, więc tablet zajmuje dużą pamięć RAM z powodu dużego ekranu?
Rahul Mandaliya
@RahulMandaliya: Przykro mi, ale nie rozumiem Twojej obawy ani tego, co ma ona wspólnego z tym pytaniem. Możesz otworzyć osobne pytanie dotyczące przepełnienia stosu, w którym szczegółowo wyjaśnisz swoje obawy.
CommonsWare
15

Jest koniec 2018 roku, więc wszystko się zmieniło.

Po pierwsze: uruchom aplikację i otwórz zakładkę Android Profiler w Android Studio. Zobaczysz, ile pamięci zużywa, będziesz zaskoczony, ale może przydzielić dużo pamięci RAM.

Również tutaj jest wielki artykuł w oficjalnych docs szczegółowe instrukcje na temat korzystania z pamięci Profiler, które może dać ci dogłębnie swojego zarządzania pamięcią.

Ale w większości przypadków wystarczy Ci zwykły Android Profiler.

wprowadź opis obrazu tutaj

Zwykle aplikacja zaczyna się od alokacji 50 MB pamięci RAM, ale natychmiast przeskakuje do 90 MB po rozpoczęciu ładowania niektórych zdjęć do pamięci. Po otwarciu Aktywności za pomocą ViewPagera z fabrycznie załadowanymi zdjęciami (po 3,5 MB każdy) możesz łatwo uzyskać 190 MB w kilka sekund.

Ale to nie znaczy, że masz problemy z zarządzaniem pamięcią.

Najlepsza rada, jaką mogę dać, to postępować zgodnie ze wskazówkami i najlepszymi praktykami, używać najlepszych bibliotek do ładowania obrazów (Glide, Picasso) i wszystko będzie dobrze.


Ale jeśli chcesz coś dostosować i naprawdę potrzebujesz wiedzieć, ile pamięci możesz przydzielić ręcznie, możesz uzyskać całkowitą wolną pamięć i obliczyć z góry określoną część (w%). W moim przypadku potrzebowałem buforować odszyfrowane zdjęcia w pamięci, więc nie muszę ich odszyfrowywać za każdym razem, gdy użytkownik przesuwa się po liście.

W tym celu możesz skorzystać z gotowej do użycia klasy LruCache . Jest to klasa pamięci podręcznej, która automatycznie śledzi ilość pamięci przydzielonej przez obiekty (lub liczbę instancji) i usuwa najstarsze, aby zachować najnowsze według historii użycia. Oto świetny samouczek, jak go używać.

W moim przypadku utworzyłem 2 instancje pamięci podręcznych: dla kciuków i załączników. Ustawiono je jako statyczne dzięki dostępowi singleton, dzięki czemu są dostępne globalnie w całej aplikacji.

klasa pamięci podręcznej:

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

Oto jak obliczam dostępną wolną pamięć RAM i ile mogę z niej ugryźć:

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

I tak używam go w adapterach, aby uzyskać buforowany obraz:

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

i jak ustawiłem go w pamięci podręcznej w wątku w tle (zwykłe AsyncTask):

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

Moja aplikacja jest ukierunkowana na API 19+, więc urządzenia nie są stare, a te części dostępnej pamięci RAM są wystarczająco dobre dla pamięci podręcznej w moim przypadku (1% i 3%).

Ciekawostka: Android nie ma żadnych interfejsów API ani innych hacków, aby uzyskać ilość pamięci przydzielonej do Twojej aplikacji, jest obliczana na bieżąco na podstawie różnych czynników.


PS Używam statycznego pola klasy do przechowywania pamięci podręcznej, ale zgodnie z najnowszymi wytycznymi Androida zaleca się użycie do tego celu komponentu architektury ViewModel .

Kirill Karmazin
źródło