2012-05-21 23 views
19

Testuję na Android 3.1, opcja dużego heapsize, około 250M dostępnej pamięci.Błąd fragmentacji pamięci Android GC. Obejście?

ustawić następujący kod do uruchomienia ilekroć wcisnął przycisk Test w preferencjach mojej aplikacji:

float [][][]foo = new float[3][2048][2048]; 
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888); 
bm.recycle(); 
bm = null; 
foo = null; 

mam dużo pamięci do tego - mogę nacisnąć przycisk kilka razy bez problemu.

Ale jeśli nadal uderzam w przycisk, w końcu (mniej niż 20 trafień) umiera z OutOfMemory. [Zwykle w android.graphics.Bitmap.nativeCreate (Native Method)]

Nic innego się nie dzieje - nigdy nie muszę opuszczać PreferencjiAktywność. Jest mała toast, który jest również wyświetlany po naciśnięciu przycisku, więc trwa mała aktywność w innym interfejsie użytkownika.

Czy to z powodu fragmentacji, czy po prostu strasznego błędu w kodzie Bitmapy i/lub GC? Czy robię coś głupiego? (Niech to będzie głupie ...)

Czy ktoś ma obejście? Ponieważ powyższe jest dość reprezentatywne dla tego, co mój kod musi zrobić za każdym razem, gdy użytkownik go wywołuje, a teraz, pomimo skrupulatnego usuwania zmiennych, umiera po kilku użyciach. (I zostało to doprowadza mnie do orzechów przez długi czas!)

[Aktualizacja]

I potwierdziły, że jest to bug problem fragmentacji lub GC, jak nora sterty pokazuje mi tylko gdy jest za pomocą 5,6M bezczynność (brak przecieków) osiąga wartość około 26 M podczas przetwarzania. (Natywny stos pozostaje poniżej 4M.) Podczas gdy sterty java w międzyczasie rośnie do limitu 280M na moim urządzeniu testowym, w którym to momencie zacząłem otrzymywać wyjątki OutOfMemory. Tak więc używam tylko 10% dostępnej sterty w szczycie, ale otrzymuję OutOfMemory.

[Dodanie wywołania do System.gc() niestety rozwiązuje prosty przypadek testowy podany powyżej. Mówię niefortunnie, ponieważ (A) nie powinno to mieć znaczenia, i (B), ponieważ już to nazywam regularnie w moim prawdziwym kodzie, więc to oznacza, że ​​mój prosty przypadek testowy powyżej jest zbyt prosty.]

Czy ktoś jeszcze biegnie zaangażowany w to? Jakiekolwiek obejścia? Czy istnieje ładny sposób na ponowne uruchomienie mojej aplikacji?

[Aktualizacja]

Kolejna wersja niezawodnie powoduje OutOfMemory w ciągu 3 do 4 wywołań (naciśnięcia przycisku):

float [][][]foo = new float[3][2048][2048]; 
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888); 
int []bar = new int[3*2048*2048]; 
bm.recycle(); 
bm = null; 
System.gc(); 
foo = null; 
System.gc(); 
bar = null; 
System.gc(); 

śledzenia pamięci przedstawia stosu rosnącego stopniowo każdego wywołania aż osiągnie limit i umiera. Jeśli usunę jedną z trzech alokacji, osiągnie równowagę i przetrwa w nieskończoność. Usunięcie wszystkich oprócz ostatniego gc() powoduje, że umrze nieco wcześniej.

Powiedziałbym, że jest to problem fragmentacji, a nie błąd gc jako taki. Jeśli ktokolwiek wie, jak to naprawić, daj mi znać. Przyporządkowanie int [] służy do zapisania mapy bitowej, więc nie mam możliwości przydzielenia jej jako tablicy 2d (ograniczenie biblioteki Android Bitmap).

+0

Nie wiem, czy to pomoże, ale dalvik korzysta z [Mark-and-Sweep] (http://www.brpreiss.com/books/opus5/html/page424.html#SECTION0014300000000000000000) typu GC. Gdzie będzie próbował poczekać, aż cała dostępna pamięć zostanie użyta, a następnie wyczyść ją na raz (problemy z wydajnością, ponieważ musi przerwać przetwarzanie do GC). Być może wywołanie 'System.gc();' jest dokładnie tym, co jest potrzebne z tym problemem, ponieważ GC oczekuje mniejszych obiektów. – Ancantus

+1

@Accantus - Niestety, jak już wspomniałem, wywołuję System.gc() po każdej operacji recycle()/null w moim prawdziwym kodzie i nadal otrzymuję to zachowanie. Myślę, że jest to problem fragmentacji lub coś takiego jak System.gc() jest ignorowane lub zachowuje się inaczej w wątkach w tle. Byłbym skłonny zabić i ponownie uruchomić moją aplikację od zera, aby skompaktować pamięć, jeśli istnieje niezawodny sposób na to. Chciałbym również, aby zadzwonić, by powiedzieć mi, po GC, jak dużo sterty java jest wolną przestrzenią (co by mi natychmiast powiedział i na pewno, jeśli jest to problem fragmentacji lub niejasny wyciek). Czy jest jeden? – Brandyn

+0

Yah, jak pewnie wiesz, nazywając 'System.gc();' po prostu zaznacza GC, aby zacząć, to niekoniecznie oznacza, że ​​będzie w ogóle działał. W rzeczywistości szukałem problemów związanych z bitmapą GC, [to pytanie SO] (http://stackoverflow.com/questions/7852943/what-does-bitmaprecycle-in-android-honeycomb-actually-do) może pomóc. – Ancantus

Odpowiedz

1

Aby zapobiec fragmentacji, wystarczy jednorazowo przydzielić dużą tablicę ORAZ bitmapę i użyć jej ponownie.

W przypadku Androida istnieją pewne zastrzeżenia, ponieważ system Android próbuje w pewnym stopniu zarządzać zasobami aplikacji.Na przykład: Activity lub View mogą zostać rozładowane, jeśli nie są widoczne, i ponownie uruchomić później, jeśli staną się ponownie widoczne. Więc duże rzeczy powinny być lepiej przechowywane przez obiekt Application lub static.

Jeśli jest to po prostu używane w niektórych oknach dialogowych preferencji, należy zarezerwować je przy pierwszym użyciu, ale zachować je później, aby nie zużywać tak dużo pamięci przy każdym uruchomieniu. Jeśli jest rzadko używany, być może powinieneś ponownie uruchomić aplikację po opuszczeniu ekranu preferencji. Użytkownik nie musi zwracać na to uwagi, jeśli jest dobrze wykonany, a Ty ponownie uzyskasz nowy, przyjazny dla pamięci proces.

+0

Niestety, nie jest to opcja Jest to aplikacja do przetwarzania obrazu, która pobiera dostarczone przez użytkownika obrazy o różnych rozmiarach (bez maksymalnej wartości max - max to cokolwiek może przecisnąć się do dostępnej pamięci). – Brandyn

+0

Nawet bez statycznej tablicy i Bitnaos nadal możesz spróbować ponownie uruchomić aplikację po jednym lub kilku zdjęciach.Może wystarczy, że opuścisz ostatnią 'Activity' przez' finish() 'i ponownie otworzysz ją przez' startActivity' .Jednak maszyna wirtualna może być ponownie wykorzystana w 'smart 'sposób, więc może być konieczne uruchomienie usługi pomocniczej przy wyjściu, która ponownie uruchomi twoją główną 'Aktywność' – dronus

+0

@Brandyn W 2016 roku problem z fragmentacją pamięci jest nadal obecny Zacząłem od używania LruCache, ale po kilku miesięcy użytkowania Widzę, że powoduje OutOfMemory z powodu fragmentacji pamięci. Patrz https://github.com/andstatus/andstatus/issues/351 Jedynym rozwiązaniem, które chcę teraz spróbować, jest przydzielenie pamięci podręcznej obrazu raz, a następnie ponowne użycie buforowanej bitmy pamięć ps. Będę musiał przydzielić bitmapy o maksymalnym rozmiarze, aby mogły być ponownie użyte ... – yvolk

4

Oto kolejny SO strona, która podobno ma obejście tego problemu:

Strange out of memory issue while loading an image to a Bitmap object

Konkretnie, odpowiedź przez Efraima (zaczerpnięty):

„1) Za każdym razem zrobić BitmapFactory. decodeXYZ(), upewnij się, że przekazałeś w BitmapFactory.Options z inPortable zestawem ustawionym na true (najlepiej z inInputShareable również ustawionym na true). RGB_8888). Mam na myśli NIGDY! Nigdy tego nie zrobiłem, nie podniosłem błędu pamięci po kilku przejściach. Żadna ilość funkcji recycle(), System.gc(), cokolwiek pomogło. Zawsze podnosi wyjątek. Innym sposobem, który faktycznie działa, jest posiadanie fikcyjnego obrazu w twoich rysunkach (lub innej Bitmapie, która została zdekodowana za pomocą kroku 1 powyżej), przeskalowanie tego, co chcesz, a następnie manipulowanie wynikową bitmapą (taką jak przekazanie jej na płótnie dla większej zabawy). Tak więc zamiast tego należy użyć: Bitmap.createScaledBitmap (srcBitmap, width, height, false). Jeśli z jakiegoś powodu trzeba użyć brutalnej siły stworzyć metodę, to przynajmniej zdać Config.ARGB_4444.”

W komentarzach, niektórzy ludzie mówili, że to rozwiązać ich problem, który jest bardzo podobny do tego z OP tutaj.

Dodam, że Diane Hackborn skomentowała, że ​​od wersji 3.0 Androida nie przydziela już bitmap z macierzystej sterty, ale zamiast tego bezpośrednio przydziela je ze zwykłej sterty, co może sprawić, że twoje rodzime sterty nie będą miały znaczenia (zobacz komentarz hackbod na tej stronie:

Bitmaps in Android

Chyba th zakłada znaczną zmianę w Honeycomb w zakresie przydzielania bitmap, co może wyjaśnić, dlaczego występują błędy z takimi przydziałami (jeśli takowe istnieją). Nie wiem, jaki wpływ ma ta zmiana na polecenie recycle(), ale w świetle powyższych uwag Ephraima odpowiedź może być "niezbyt dobra".

Wreszcie

Aby korzystać largeHeap łykać ogromne mapy bitowe mogą być postrzegane jako nie gra ładny z innych aplikacji, zwłaszcza jeśli masz zamiar blisko do fizycznych granic urządzenia. Nie jestem pewien, jak możesz tego uniknąć, ale przygotuj się na wiele działań onPause()/onResume(), gdy aplikacja postępuje w innych aplikacjach, a oni wracają do twoich. Ten SO odpowiedź zawiera omówienie tego:

Detect application heap size in Android

+0

'inPurgeable' jest teraz przestarzałe i ignorowane. – zyamys

-2

Jest bardzo szczegółowy artykuł o zarządzaniu pamięć bitmap na Android developer.android.com:

http://developer.android.com/training/displaying-bitmaps/manage-memory.html

Zasadniczo zalecają używać Bitmap.recycle() w celu uniknięcia błędów OutOfMemoryError dla systemu Android 2.3.3 i niższych.Zgodnie z dokumentacją uwalnia on obiekt macierzysty powiązany z Obiektem Bitmap.

+0

Używa 3.1, gdzie Bitmap.recycle() nie musi już być wywoływana bezpośrednio. –

Powiązane problemy