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.
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.
Odpowiedzi:
Istnieją dwa sposoby myślenia o wyrażeniu „dostępny rozmiar sterty aplikacji”:
Ile sterty może użyć moja aplikacja, zanim zostanie wyzwolony twardy błąd? I
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:
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:
Również (według Kishore w komentarzu poniżej):
I (zgodnie z komentarzem akauppi):
Zgodnie z komentarzem cmcromance:
I (komentarze procentu):
Inne urządzenia
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.
Debug.getNativeHeapSize()
chyba załatwi sprawę. Jednak istnieje od 1.0.
Debug
Klasa 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()
.
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);
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.
getMemoryClass
wydaje się sugerować, że liczba może nie być taka sama jak dostępny rozmiar sterty dla twojego vm, a ponieważ dokument dla getNativeHeapSize
jest ... milczący, naprawdę uważam, że Runtime.getRuntime().maxMemory()
jest najlepszą odpowiedzią.
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();
}
}
AvailableMemoryPercentage
jest według wzoru: ile pamięci urządzenia jest aktualnie wolne. MIN_AVAILABLE_MEMORY_PERCENTAGE
jest twoim parametrem niestandardowym, progiem, przy którym zaczynasz czekać, aż garbage collector wykona swoje zadanie.
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ść
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.
Runtime rt = Runtime.getRuntime();
rt.maxMemory()
wartość to b
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()
wartość to MB
rt
? Gdzie jest to zadeklarowane?