Wykryj rozmiar sterty aplikacji w systemie Android

145

Jak programowo wykrywać rozmiar sterty aplikacji dostępny dla aplikacji na Androida?

Słyszałem, że w późniejszych wersjach SDK jest funkcja, która to robi. W każdym razie szukam rozwiązania, które działa od wersji 1.5 i nowszej.

hpique
źródło
2
Związane z: stackoverflow.com/questions/1518154/…
Josh Lee,

Odpowiedzi:

453

Istnieją dwa sposoby myślenia o wyrażeniu „dostępny rozmiar sterty aplikacji”:

  1. Ile sterty może użyć moja aplikacja, zanim zostanie wyzwolony twardy błąd? I

  2. Ile stosu powinna zużywać moja aplikacja, biorąc pod uwagę ograniczenia wersji systemu operacyjnego Android i sprzętu urządzenia użytkownika?

Istnieje inna metoda określania każdego z powyższych.

W przypadku punktu 1 powyżej: maxMemory()

które można wywołać (np. w onCreate()metodzie twojego głównego działania ) w następujący sposób:

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

Ta metoda informuje, ile łącznie bajtów sterty może używać Twoja aplikacja .

W przypadku punktu 2 powyżej: getMemoryClass()

które można wywołać w następujący sposób:

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

Ta metoda mówi w przybliżeniu, ile megabajtów stosu powinna wykorzystać twoja aplikacja, jeśli chce odpowiednio przestrzegać ograniczeń obecnego urządzenia i praw innych aplikacji do działania bez wielokrotnego zmuszania do onStop()/ onResume()cycle, ponieważ są niegrzeczne wypłukane z pamięci, gdy Twoja aplikacja elephantine bierze kąpiel w jacuzzi z systemem Android.

O ile wiem, to rozróżnienie nie jest jasno udokumentowane, ale przetestowałem tę hipotezę na pięciu różnych urządzeniach z Androidem (patrz poniżej) i z satysfakcją potwierdziłem, że jest to poprawna interpretacja.

W przypadku standardowej wersji Androida, maxMemory()zwykle zwraca mniej więcej taką samą liczbę megabajtów, jak wskazano w getMemoryClass()(tj. Około milion razy większa od ostatniej wartości).

Jedyna sytuacja (o której jestem świadomy), w której te dwie metody mogą się różnić, dotyczy zrootowanego urządzenia z wersją Androida, takiego jak CyanogenMod, który pozwala użytkownikowi ręcznie wybrać, jak duży rozmiar sterty powinien być dozwolony dla każdej aplikacji. Na przykład w CM ta opcja pojawia się w „CyanogenMod settings” / „Performance” / „VM heap size”.

UWAGA: PAMIĘTAJ, ŻE RĘCZNE USTAWIENIE TEGO WARTOŚCI MOŻE WYSŁAĆ WIADOMOŚĆ DO TWOJEGO SYSTEMU, SZCZEGÓLNIE jeśli wybierzesz mniejszą wartość niż jest to normalne dla Twojego urządzenia.

Oto moje wyniki testu pokazujące wartości zwracane przez maxMemory()i getMemoryClass()dla czterech różnych urządzeń z uruchomionym CyanogenMod, przy użyciu dwóch różnych (ręcznie ustawionych) wartości sterty dla każdego:

  • G1:
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 16 MB:
      • maxMemory: 16777216
      • getMemoryClass: 16
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 16
  • Moto Droid:
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 24
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 16 MB:
      • maxMemory: 16777216
      • getMemoryClass: 24
  • Nexus One:
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 32 MB:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 24 MB:
      • maxMemory: 25165824
      • getMemoryClass: 32
  • Viewsonic GTab:
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 32:
      • maxMemory: 33554432
      • getMemoryClass: 32
    • Z rozmiarem sterty maszyny wirtualnej ustawionym na 64:
      • maxMemory: 67108864
      • getMemoryClass: 32

Oprócz powyższego, testowałem na tablecie Novo7 Paladin z Ice Cream Sandwich. Zasadniczo była to standardowa wersja ICS, z tym wyjątkiem, że zrootowałem tablet za pomocą prostego procesu, który nie zastępuje całego systemu operacyjnego, aw szczególności nie zapewnia interfejsu, który pozwoliłby na ręczne dostosowanie rozmiaru sterty.

Oto wyniki dla tego urządzenia:

  • Novo7
    • maxMemory: 62914560
    • getMemoryClass: 60

Również (według Kishore w komentarzu poniżej):

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

I (zgodnie z komentarzem akauppi):

  • Samsung Galaxy Core Plus
    • maxMemory: (nie określono w komentarzu)
    • getMemoryClass: 48
    • largeMemoryClass: 128

Zgodnie z komentarzem cmcromance:

  • Galaxy S3 (Jelly Bean) duża kupa
    • maxMemory: 268435456
    • getMemoryClass: 64

I (komentarze procentu):

  • LG Nexus 5 (4.4.3) normalny
    • maxMemory: 201326592
    • getMemoryClass: 192
  • LG Nexus 5 (4.4.3) duży stos
    • maxMemory: 536870912
    • getMemoryClass: 192
  • Galaxy Nexus (4.3) normalny
    • maxMemory: 100663296
    • getMemoryClass: 96
  • Galaxy Nexus (4.3) duża sterta
    • maxMemory: 268435456
    • getMemoryClass: 96
  • Galaxy S4 Play Store Edition (4.4.2) normalny
    • maxMemory: 201326592
    • getMemoryClass: 192
  • Galaxy S4 Play Store Edition (4.4.2) duża kupa
    • maxMemory: 536870912
    • getMemoryClass: 192

Inne urządzenia

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

Nie testowałem tych dwóch metod przy użyciu specjalnej opcji manifestu android: largeHeap = "true" dostępnej od Honeycomb, ale dzięki cmcromance i tencent mamy kilka przykładowych wartości largeHeap, jak opisano powyżej.

Moim oczekiwaniem (które wydaje się być wspierane przez powyższe liczby largeHeap) byłoby to, że ta opcja miałaby efekt podobny do ręcznego ustawienia sterty przez zrootowany system operacyjny - tj. Podniosłaby wartość maxMemory()przy opuszczaniugetMemoryClass() samą. Istnieje inna metoda, getLargeMemoryClass (), która wskazuje, ile pamięci jest dopuszczalne dla aplikacji korzystającej z ustawienia largeHeap. Dokumentacja dotycząca metody getLargeMemoryClass () stwierdza, że ​​„większość aplikacji nie powinna potrzebować takiej ilości pamięci i zamiast tego powinna przestrzegać limitu getMemoryClass ()”.

Jeśli zgadłem poprawnie, użycie tej opcji przyniosłoby takie same korzyści (i niebezpieczeństwa), jak użycie miejsca udostępnionego przez użytkownika, który zwiększył stertę za pośrednictwem zrootowanego systemu operacyjnego (tj. Jeśli Twoja aplikacja korzysta z dodatkowej pamięci, prawdopodobnie nie będzie działał tak dobrze z innymi aplikacjami, które użytkownik uruchamia w tym samym czasie).

Zauważ, że klasa pamięci najwyraźniej nie musi być wielokrotnością 8 MB.

Z powyższego widać, że getMemoryClass()wynik jest niezmienny dla danej konfiguracji urządzenia / systemu operacyjnego, natomiast wartość maxMemory () zmienia się, gdy sterta jest inaczej ustawiana przez użytkownika.

Moje własne praktyczne doświadczenie jest takie, że na G1 (który ma klasę pamięci 16), jeśli ręcznie wybiorę 24 MB jako rozmiar sterty, mogę działać bez błędów, nawet jeśli moje użycie pamięci może wzrosnąć do 20 MB (prawdopodobnie może do 24 MB, chociaż nie próbowałem tego). Ale inne podobnie duże aplikacje mogą zostać wypłukane z pamięci w wyniku marnotrawstwa mojej własnej aplikacji. I odwrotnie, moja aplikacja może zostać opróżniona z pamięci, jeśli te inne wymagające konserwacji aplikacje zostaną przeniesione na pierwszy plan przez użytkownika.

Nie możesz więc przekroczyć ilości pamięci określonej przez maxMemory(). I powinieneś starać się nie przekraczać limitów określonych przez getMemoryClass(). Jednym ze sposobów, aby to zrobić, jeśli wszystko inne zawiedzie, może być ograniczenie funkcjonalności takich urządzeń w sposób oszczędzający pamięć.

Na koniec, jeśli planujesz przekroczyć liczbę megabajtów określoną w getMemoryClass(), radzę pracować długo i ciężko nad zapisywaniem i przywracaniem stanu aplikacji, aby doświadczenie użytkownika było praktycznie nieprzerwane, jeśli wystąpi cykl onStop()/ onResume().

W moim przypadku ze względu na wydajność ograniczam moją aplikację do urządzeń z systemem 2.2 lub nowszym, co oznacza, że ​​prawie wszystkie urządzenia z moją aplikacją będą miały klasę pamięci 24 lub wyższą. Mogę więc zaprojektować tak, aby zajmował do 20 MB sterty i mieć pewność, że moja aplikacja będzie dobrze współpracować z innymi aplikacjami, które użytkownik może uruchamiać w tym samym czasie.

Ale zawsze będzie kilku zrootowanych użytkowników, którzy załadowali wersję 2.2 lub nowszą Androida na starsze urządzenie (np. G1). Kiedy napotkasz taką konfigurację, najlepiej powinieneś zmniejszyć zużycie pamięci, nawet jeśli maxMemory()mówi ci, że możesz przejść znacznie wyżej niż 16 MB, które getMemoryClass()mówi ci, że powinieneś być celem. A jeśli nie możesz niezawodnie zapewnić, że Twoja aplikacja będzie działać w ramach tego budżetu, przynajmniej upewnij się, że onStop()/ onResume()działa bezproblemowo.

getMemoryClass(), jak wskazała Diane Hackborn (hackbod) powyżej, jest dostępny tylko do poziomu API 5 (Android 2.0), więc, jak radzi, można założyć, że fizyczny sprzęt dowolnego urządzenia z wcześniejszą wersją systemu operacyjnego jest zaprojektowany aby optymalnie obsługiwać aplikacje zajmujące miejsce na stercie nie większe niż 16 MB.

Natomiast maxMemory(), zgodnie z dokumentacją, jest dostępny przez całą drogę z powrotem do poziomu API 1. maxMemory()Na wstępnej wersji 2.0, prawdopodobnie będzie zwracać wartość 16MB, ale zrobić zobaczyć, że w moich (dużo później) wersji CyanogenMod użytkownik można wybrać wartość sterty tak niską, jak 12 MB, co przypuszczalnie skutkowałoby niższym limitem sterty, dlatego sugerowałbym, abyś kontynuował testowanie maxMemory()wartości, nawet dla wersji systemu operacyjnego wcześniejszych niż 2.0. Możesz nawet odmówić uruchomienia w mało prawdopodobnym przypadku, gdy ta wartość jest nawet mniejsza niż 16 MB, jeśli potrzebujesz więcej niż maxMemory()wskazuje, że jest dozwolone.

Carl
źródło
2
@Carl Witaj Carl, dziękuję za wspaniały post. I przetestowałem opcję android: largeHeap = "true" na Galaxy S3 z JellyBean. Oto wynik. [maxMemory: 256.0MB, memoryClass: 64MB] (maxMemory to 64MB bez opcji largeheap)
cmcromance
1
@Carl Haha To wielki zaszczyt! :)
cmcromance
1
Dla HTC One X: max Pamięć: 67108864 PamięćKlasa: 64
Kishore
1
LG Nexus 5 (4.4.3) (normalny): maxMemory: 201326592, memoryClass: 192 | LG Nexus 5 (4.4.3) (duża sterta): maxMemory: 536870912, memoryClass: 192
ian.shaun.thomas
1
Bardzo ładnie udokumentowane. Podaj to Androidowi. Nie mogę znaleźć takiego odpowiedniego dokumentu dotyczącego Androida
Jimit Patel
20

Oficjalne API to:

Zostało to wprowadzone w wersji 2.0, gdzie pojawiły się większe urządzenia pamięci. Możesz założyć, że urządzenia z wcześniejszymi wersjami systemu operacyjnego używają oryginalnej klasy pamięci (16).

hackbod
źródło
13

Debug.getNativeHeapSize()chyba załatwi sprawę. Jednak istnieje od 1.0.

DebugKlasa ma wiele wspaniałych sposobów śledzenia przydziałów i innych problemów z wydajnością. Ponadto, jeśli chcesz wykryć sytuację z małą ilością pamięci, sprawdź Activity.onLowMemory().

Neil Traft
źródło
Dzięki Neil. Czy to działa, gdy aplikacja działa z wyłączonym debugowaniem?
hpique
2
Jestem w tym całkiem nowy, ale nie sądzę, że sterta rodzima jest taka sama jak sterta aplikacji (Dalvik), do której odnosi się pytanie. Natywna sterta zapewnia kopię zapasową danych bitmapowych, które są przydzielane przez kod natywny, podczas gdy sterta aplikacji przechowuje dane aplikacji Java. Byłem zainteresowany, aby dowiedzieć się, że natywna sterta jest wliczana do limitu sterty aplikacji, a po 3.0 alokacje faktycznie występują na stercie aplikacji. Diane Hackborn (hackbod) publikuje na ten temat tutaj: stackoverflow.com/questions/1945142/bitmaps-in-android Ale nawet w wersji 3.0 na stercie aplikacji znajdują się również inne niż „natywne” dane.
Carl
1
Od niedawnej aktualizacji Androida (być może KitKat 4.4) mapy bitowe znajdują się w stercie JVM.
akauppi,
13

Oto jak to robisz:

Uzyskanie maksymalnego rozmiaru sterty, z którego może korzystać aplikacja:

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

Pobieranie ilości stosu aktualnie używanego przez aplikację:

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

Pobieranie ilości stosu, z którego może teraz korzystać aplikacja (dostępna pamięć):

long availableMemory=maxMemory-usedMemory;

Aby ładnie sformatować każdy z nich, możesz użyć:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize); 
programista Androida
źródło
5

Zwraca maksymalny rozmiar sterty w bajtach:

Runtime.getRuntime().maxMemory()

Używałem ActivityManager.getMemoryClass (), ale na CyanogenMod 7 (gdzie indziej go nie testowałem) zwraca nieprawidłową wartość, jeśli użytkownik ręcznie ustawia rozmiar sterty.

fhucho
źródło
Ponieważ dokument dla getMemoryClasswydaje się sugerować, że liczba może nie być taka sama jak dostępny rozmiar sterty dla twojego vm, a ponieważ dokument dla getNativeHeapSizejest ... milczący, naprawdę uważam, że Runtime.getRuntime().maxMemory()jest najlepszą odpowiedzią.
BoD
3

Niektóre operacje są szybsze niż menedżer przestrzeni sterty Java. Opóźnienie operacji przez pewien czas może zwolnić miejsce w pamięci. Możesz użyć tej metody, aby uniknąć błędu rozmiaru sterty:

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
źródło
testowany dobry. Czy możesz wyjaśnić te dwie rzeczy: availableMemoryPercentage i MIN_AVAILABLE_MEMORY_PERCENTAGE? jakieś wyjaśnienia?
Noor Hossain
1
AvailableMemoryPercentagejest według wzoru: ile pamięci urządzenia jest aktualnie wolne. MIN_AVAILABLE_MEMORY_PERCENTAGEjest twoim parametrem niestandardowym, progiem, przy którym zaczynasz czekać, aż garbage collector wykona swoje zadanie.
Zon
1

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

Popełniłem błąd, tworząc prototyp mojej gry na Nexusie 7, a potem odkryłem, że prawie natychmiast zabrakło jej pamięci na ogólnym tablecie mojej żony 4.04 (klasa pamięci 48, maksymalna pamięć 50331648)

Będę musiał zrestrukturyzować mój projekt, aby załadować mniej zasobów, gdy uznam, że klasa pamięci jest niska.
Czy w Javie jest sposób, aby zobaczyć aktualny rozmiar sterty? (Widzę to wyraźnie w logCat podczas debugowania, ale chciałbym zobaczyć to w kodzie, aby dostosować, na przykład jeśli currentheap> (maxmemory / 2) rozładuj wysokiej jakości mapy bitowe załaduj niską jakość

Tkraindesigns
źródło
Na wspomnianym Nexus7-2013 getLargeMemoryClass () = 512.
akauppi
0

Czy masz na myśli programowo, czy tylko podczas programowania i debugowania? Jeśli to drugie, możesz zobaczyć te informacje z perspektywy DDMS w Eclipse. Kiedy twój emulator (prawdopodobnie nawet fizyczny telefon, który jest podłączony) jest uruchomiony, wyświetli listę aktywnych procesów w oknie po lewej stronie. Możesz go wybrać i istnieje opcja śledzenia alokacji sterty.

Steve Haley
źródło
Mam na myśli programowo. Wyjaśnił pytanie. Dzięki.
hpique
1
Proponuję spojrzeć na ten post jednego z programistów platformy Android: stackoverflow.com/questions/2298208/ ...
Steve Haley
0
Runtime rt = Runtime.getRuntime();
rt.maxMemory()

wartość to b

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

wartość to MB

qinqie
źródło
Jaki rodzaj rt? Gdzie jest to zadeklarowane?
FindOut_Quran