Kolektor śmieci w systemie Android

108

Widziałem wiele odpowiedzi na Androida, które sugerowały dzwonienie do garbage collectora w niektórych sytuacjach.

Czy dobrą praktyką jest zażądanie modułu odśmiecania pamięci w systemie Android przed wykonaniem wymagającej pamięci operacji? Jeśli nie, czy mam zadzwonić tylko wtedy, gdy otrzymam OutOfMemorybłąd?

Czy są inne rzeczy, których powinienem użyć przed skorzystaniem z urządzenia odśmiecającego?

hpique
źródło

Odpowiedzi:

144

Dla wersji starszych niż 3.0 plaster miodu : Tak, zadzwoń System.gc() .

Próbowałem utworzyć mapy bitowe, ale zawsze otrzymywałem komunikat „Błąd pamięci maszyny wirtualnej”. Ale kiedy zadzwoniłem System.gc()pierwszy, było OK.

Podczas tworzenia map bitowych system Android często kończy się niepowodzeniem z powodu braku pamięci i nie próbuje najpierw zbierać elementów bezużytecznych . Dlatego zadzwoń System.gc(), a będziesz mieć wystarczająco dużo pamięci, aby utworzyć mapy bitowe.

W przypadku tworzenia obiektów myślę, że w System.gcrazie potrzeby zostanie wywołany automatycznie, ale nie do tworzenia map bitowych. Po prostu zawodzi.

Dlatego zalecam ręczne wywoływanie System.gc()przed utworzeniem map bitowych.

steve
źródło
39
Dzieje się tak, ponieważ dane bitmapowe sprzed plastra miodu nie są przechowywane w maszynie wirtualnej, a więc nie wyzwalają GC. Nie powinno to stanowić problemu w Honeycomb i później.
Timmmm
2
@Timmmm, ale to był właściwie mój problem, po prostu ustawiłem largeheap na true.
Lei Leyba
118

Ogólnie rzecz biorąc, w obecności garbage collectora ręczne wywoływanie GC nigdy nie jest dobrą praktyką. GC jest zorganizowany wokół algorytmów heurystycznych, które działają najlepiej, gdy są pozostawione samym sobie. Ręczne wywoływanie GC często zmniejsza wydajność.

Czasami , w niektórych stosunkowo rzadkich sytuacjach, może się okazać, że konkretny GC popełnia błąd, a ręczne wywołanie GC może wtedy poprawić sytuację pod względem wydajności. Dzieje się tak, ponieważ nie jest tak naprawdę możliwe zaimplementowanie „doskonałego” GC, który będzie optymalnie zarządzać pamięcią we wszystkich przypadkach. Takie sytuacje są trudne do przewidzenia i zależą od wielu subtelnych szczegółów implementacji. „Dobra praktyka” polega na tym, aby GC działało samodzielnie; ręczne wezwanie do Sądu jest wyjątkiem, który należy przewidzieć dopiero po należytym zaobserwowaniu rzeczywistego problemu z wydajnością.

Thomas Pornin
źródło
8
+1 za dobrą odpowiedź niezależną od platformy. Czekam, zanim ktoś wybierze, czy znajdzie odpowiedź dotyczącą Androida.
hpique
8
Zgadzam się z tym, co napisałeś, jeśli pozwolisz, aby „wydajność” oznaczała coś bardziej ogólnego niż wydajność procesora. Na urządzeniu z Androidem postrzeganie wydajności przez użytkownika jest najważniejsze. Deweloper może preferować uruchamianie GC, gdy użytkownik na przykład naciska przyciski, aby użytkownik nie był świadomy GC.
Prezydent James K. Polk
5
W grach często ważne jest, aby GC nie działało, gdy gra jest uruchomiona (wymaga to, aby wszystkie obiekty były wstępnie utworzone i przetworzone lub podobne), ale gdy gra jest wstrzymana, jest to dobry moment na GC.
Pat
4
Dane bitmapowe sprzed plastra miodu nie są przechowywane w pamięci maszyny wirtualnej. System nie wykryje użycia zbyt dużej ilości pamięci innej niż VM z powodu bitmap i uruchomi GC (który uruchomiłby Bitmap.finalize()metody, które zwalniają pamięć inną niż VM). Dlatego w tym przypadku powinieneś przejść przez głowę GC i biec Bitmap.recycle()lub System.gc()gdzie to stosowne. Ale tylko przed plastrem miodu.
Timmmm
1
@ThomasPornin - Z drugiej strony, jako programista aplikacji, wiesz coś, czego system operacyjny nie zna: czasy, w których Twoja aplikacja ma teraz dokonać poważnych zmian w sposobie wykorzystania pamięci i że byłoby mniej uciążliwe dla doświadczenie użytkownika, aby zatrzymać się teraz, niż w dowolnym momencie w przyszłości . Oznacza to, że rozsądne może być wykonywanie rzadkich połączeń podczas większych zmian w sposobie korzystania z aplikacji. Są one rzadkie w tym sensie, że nie należy często dzwonić do GC, ale są powszechne, ponieważ prawie każda znacząca aplikacja ma tak znane z projektu momenty.
ToolmakerSteve
26

Brak pamięci w aplikacji na Androida jest bardzo częsty, jeśli nie obsługujemy poprawnie mapy bitowej, rozwiązaniem problemu byłoby

if(imageBitmap != null) {
    imageBitmap.recycle();
    imageBitmap = null;
}
System.gc();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;
imageBitmap = BitmapFactory.decodeFile(URI, options);
Bitmap  scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, 200, 200, true);
imageView.setImageBitmap(scaledBitmap);

W powyższym kodzie Właśnie próbowałem przetworzyć bitmapę, co pozwoli ci zwolnić używaną przestrzeń pamięci, więc brak pamięci może się nie zdarzyć. Próbowałem to zadziałało dla mnie.

Jeśli nadal masz problem, możesz również dodać tę linię

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];
options.inPurgeable = true;

więcej informacji znajdziesz pod tym linkiem

https://web.archive.org/web/20140514092802/http://voices.yahoo.com/android-virtual-machine-vm-out-memory-error-7342266.html?cat=59


UWAGA: Ze względu na chwilową "pauzę" spowodowaną wykonaniem gc tak jest nie zaleca się robienia tego przed każdą alokacją bitmapy.

Optymalny projekt to:

  1. Zwolnij wszystkie mapy bitowe, które nie są już potrzebne , zgodnie z if / recycle / nullwyświetlonym kodem. (Wymyśl metodę, która pomoże w tym).

  2. System.gc();

  3. Przydziel nowe mapy bitowe.

jogesh
źródło
19

Jeśli otrzymasz OutOfMemoryError, zwykle jest za późno, aby zadzwonić do modułu odśmiecania pamięci ...

Oto cytat od programisty Androida:

W większości przypadków odśmiecanie odbywa się z powodu ton małych, krótkotrwałych obiektów, a niektóre odśmiecacze, takie jak generacyjne odśmiecacze, mogą zoptymalizować zbieranie tych obiektów, aby aplikacja nie była zbyt często przerywana. Moduł odśmiecania pamięci dla systemu Android nie jest niestety w stanie wykonać takich optymalizacji, a zatem tworzenie krótkotrwałych obiektów w ścieżkach kodu o krytycznym znaczeniu dla wydajności jest bardzo kosztowne dla aplikacji.

Tak więc, według mojego zrozumienia, nie ma pilnej potrzeby wzywania gc. Lepiej poświęcić więcej wysiłku na unikanie niepotrzebnego tworzenia obiektów (np. Tworzenia obiektów wewnątrz pętli)

Andreas Dolk
źródło
6
Czy cytatu nie można interpretować w inny sposób? Może trzeba wywołać GC ręcznie, aby zebrać te obiekty, zanim zużyją zbyt dużo pamięci.
hpique
@hgpc: Nie widzę, jak można zinterpretować cytat w sposób, który sugerujesz. Domyślam się, że dokumentacja jest przyznaniem się do przyznania się, że ich GC jest prosta; kiedy zaczyna brakować pamięci, wykonywany jest pełny GC.
Prezydent James K. Polk
Pełne wykorzystanie złożonych obiektów i struktur wykorzystujących złożone obiekty pozwala na lepsze wykorzystanie czasu programowania niż przedwczesna optymalizacja. Martwienie się o optymalizację jest łatwiejszą pułapką dla Androida niż dla Enterprise Java, ponieważ podświadomie myślimy o wydajności podczas kodowania aplikacji mobilnej. Zwłaszcza, że ​​programiści frameworków, którzy muszą myśleć o wydajności, przeciekają ten punkt widzenia do swojej oficjalnej dokumentacji, gdy nie są ostrożni.
LateralFractal
9

Wygląda na System.gc()to, że nie działa na Art Android 6.0.1 Nexus 5x, więc Runtime.getRuntime().gc();zamiast tego używam .

cmicat
źródło
1
System.gc()jest funkcją opakowującą dla Runtime.getRuntime().gc(). Zobacz android.googlesource.com/platform/libcore/+/…
Nathan F.
Tak, ze źródła i dokumentacji wygląda na to, że są takie same, ale rzeczywiście System.gc()nie działa dla mnie, ale Runtime.getRuntime().gc()działa!
Ivan
7

Moja aplikacja zarządza wieloma obrazami i przestała działać z powodu błędu OutOfMemoryError. To mi pomogło. W pliku Manifest.xml Add

<application
....
   android:largeHeap="true"> 
Klykoo
źródło
9
Google tego nie lubi
Pedro Paulo Amorim
1
@PedroPauloAmorim wyjaśnij, dlaczego?
Machado
2
Nie używaj largeHeap, jeśli nie masz pewności, co robisz. Używanie dużych stert sprawia, że ​​garbage collector pracuje znacznie ciężej, ponieważ musi przepuścić wiele niepotrzebnych danych, zanim wyczyści pamięć.
Prakash
7

Ogólnie rzecz biorąc, nie powinieneś wywoływać GC jawnie za pomocą System.gc (). Jest nawet wykład IO ( http://www.youtube.com/watch?v=_CruQY55HOk ), w którym wyjaśniają, co oznacza GC pauzy log, iw którym również stwierdzają, że nigdy nie wywołują System.gc (), ponieważ Dalvik wie lepiej niż ty, kiedy to zrobić.

Z drugiej strony, jak wspomniano w powyższych odpowiedziach, już proces GC w systemie Android (jak wszystko inne) jest czasami błędny. Oznacza to, że algorytmy Dalvik GC nie są na równi z JVM Hotspot lub JRockit i mogą czasami powodować błędy. Jedną z takich sytuacji jest przydzielanie obiektów bitmapowych. Jest to trudne, ponieważ używa pamięci Heap i Non Heap oraz ponieważ jedno luźne wystąpienie obiektu bitmapowego na urządzeniu z ograniczoną pamięcią wystarczy, aby uzyskać wyjątek OutOfMemory. Dlatego nazywanie go po tym, jak nie potrzebujesz już tej mapy bitowej, jest ogólnie sugerowane przez wielu programistów, a niektórzy nawet uważają je za dobrą praktykę.

Lepszą praktyką jest użycie .recycle () na mapie bitowej, ponieważ jest to cel tej metody, ponieważ oznacza ona pamięć natywną mapy bitowej jako bezpieczną do usunięcia. Należy pamiętać, że jest to bardzo zależne od wersji, co oznacza, że ​​będzie generalnie wymagane na starszych wersjach Androida (myślę, że Pre 3.0), ale nie będzie wymagane w późniejszych wersjach. Również nie zaszkodzi używanie go na nowszych wersjach ether (po prostu nie rób tego w pętli lub coś w tym stylu). Nowe środowisko wykonawcze ART bardzo się tutaj zmieniło, ponieważ wprowadzono specjalną „partycję” sterty dla dużych obiektów, ale myślę, że nie zaszkodzi to zrobić z ART ether.

Również jedna bardzo ważna uwaga na temat System.gc (). Ta metoda nie jest poleceniem, na które Dalvik (lub maszyny JVM) są zobowiązani odpowiedzieć. Potraktuj to bardziej tak, jakbyś powiedział do maszyny wirtualnej „Czy mógłbyś wykonać czyszczenie pamięci, jeśli nie jest to kłopotliwe”.

Igor Čordaš
źródło
3

Powiedziałbym nie, ponieważ programista dokumentuje stan użycia pamięci RAM :

...
GC_EXPLICIT

Jawny GC, na przykład gdy wywołujesz gc () (którego powinieneś unikać wywoływania i zamiast zaufać GC, aby uruchomić w razie potrzeby).

...

Pogrubioną czcionką zaznaczyłem odpowiednią część.

Przyjrzeć się serii YouTube Wzory Android Wydajność - pokaże wskazówki dotyczące zarządzania zużycie pamięci o aplikacji (takich jak korzystanie z Androidem ArrayMaps i SparseArrays zamiast HashMaps).

Farbod Salamat-Zadeh
źródło
3

Krótka uwaga dla programistów Xamarin .

Jeśli chcesz dzwonić System.gc()w aplikacjach Xamarin.Android, powinieneś zadzwonićJava.Lang.JavaSystem.Gc()

Denis Gordin
źródło
2

Nie ma potrzeby wywoływania modułu odśmiecania pamięci po wykonaniu pliku OutOfMemoryError.

To Javadoc wyraźnie stwierdza:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

Zatem moduł odśmiecania już próbował zwolnić pamięć przed wygenerowaniem błędu, ale nie powiodło się.

perdian
źródło
8
Android Javadoc tego nie stwierdza i najprawdopodobniej jego implementacja jest inna. d.android.com/reference/java/lang/OutOfMemoryError.html
hpique
Masz rację, że to nie dotyczy Androida. Ale z drugiej strony nie możesz obsłużyć OOE w runtime ether, więc nie możesz wiele z tym zrobić, nawet jeśli to była / nie była prawda.
Igor Čordaš