Jakie narzędzia i metody systemu Android najlepiej sprawdzają się w wykrywaniu wycieków pamięci / zasobów? [Zamknięte]


152

Mam opracowaną aplikację na Androida i jestem na etapie rozwoju aplikacji na telefon, w którym wszystko wydaje się działać dobrze, a Ty chcesz ogłosić zwycięstwo i statek, ale wiesz, że muszą być tylko wycieki pamięci i zasobów tam; a na Androidzie jest tylko 16 MB sterty, a jej pozornie zaskakująco łatwe do wycieku w aplikacji na Androida.

Rozejrzałem się i do tej pory udało mi się tylko znaleźć informacje o „hprof” i „traceview”, ale żadne z nich nie ma wielu pochlebnych recenzji.

Jakie narzędzia lub metody znalazłeś lub opracowałeś i chciałbyś udostępnić w projekcie systemu operacyjnego?


3
Czy mogę głosować za tym, żeby Р̀СТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ zmienił jego nazwisko na coś czytelnego dla człowieka.
JPM

1
czy byłoby w porządku, aby ponownie otworzyć to pytanie, jeśli usuniemy słowo „Android Tools” z tytułu pytania? Znalezione tutaj odpowiedzi były bardzo przydatne, aby rozwiązać problemy z wyciekiem pamięci
k3b,

OK, więc użytkownik ciągle przełącza się między czynnościami, co oznacza, że ​​mógł zamienić 15 czynności w ciągu 20 sekund. Czy to może być przyczyną błędu braku pamięci? Co mam zrobić, żeby to naprawić? Dzięki!
Ruchir Baronia

1
Nie mogę podać tego jako odpowiedzi, ponieważ pytanie zostało zamknięte, jednak polecam zajrzeć do Leak Canary . Po prostu użyj aplikacji, otwieraj i zamykaj zadania i pozwól bibliotece wykonać swoją pracę. Dowiesz się nawet o miejscu wycieku. Daj analizatorowi nieszczelności trochę czasu na wykonanie pracy po wystąpieniu wycieku - zwykle zajmuje to około 2 minuty lub więcej, zanim zostanie znalezione źródło wycieku. Następnie przedstawi ci go starannie w aplikacji. Żadne dodatkowe narzędzia nie są potrzebne!
ubuntudroid

Odpowiedzi:


90

Jednym z najczęstszych błędów, które znalazłem podczas tworzenia aplikacji na Androida, jest błąd „java.lang.OutOfMemoryError: rozmiar mapy bitowej przekracza budżet maszyny wirtualnej”. Znalazłem ten błąd często w działaniach wykorzystujących wiele bitmap po zmianie orientacji: działanie jest niszczone, tworzone ponownie, a układy są „zawyżane” z pliku XML zużywającego pamięć maszyny wirtualnej dostępną dla map bitowych.

Mapy bitowe w poprzednim układzie działania nie są odpowiednio zwalniane przez moduł odśmiecania pamięci, ponieważ krzyżowały się z nimi odniesienia do ich działania. Po wielu eksperymentach znalazłem całkiem dobre rozwiązanie tego problemu.

Najpierw ustaw atrybut „id” w widoku nadrzędnym układu XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Następnie, w metodzie onDestroy () Twojego Activity, wywołaj metodę unbindDrawables () przekazującą referencję do nadrzędnego widoku, a następnie wykonaj System.gc ()

@Override
protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Ta metoda unbindDrawables () bada drzewo widoku rekurencyjnie i:

  1. Usuwa wywołania zwrotne ze wszystkich wad w tle
  2. Usuwa elementy podrzędne w każdej grupie widoków

3
Dobre rozwiązanie typowego problemu.
While-E

9
Nie działa to w przypadku podklas AdapterView (ListView, GridView itp.).
Arjun

@Arjun Tak, nie działa w przypadku podklas AdapterView. W tym celu musisz obsłużyć wyjątek. Reszta działa dobrze. Tego właśnie używam w moim kodzie i działa dobrze. Mam nadzieję że to pomoże.
hp.android

@ hp.android Czy masz przykład „obsłużyć to w wyjątku”? Pytam, bo mam strony z obrazkami PageAdapteri ten błąd mnie
niepokoi

4
@Jackson po prostu zmień warunek: jeśli (zobacz instancję ViewGroup &&! (Zobacz instancję AdapterView)) to pozbędzie się wyjątku, który otrzymujesz dla Adaptera
hp.android


28

Głównie dla podróżników Google z przyszłości:

Większość narzędzi java niestety nie nadaje się do tego zadania, ponieważ analizują one tylko stertę JVM. Każda aplikacja na Androida ma jednak również natywną stertę, która również musi mieścić się w limicie ~ 16 MB. Zwykle jest używany na przykład do danych bitmapowych. Możesz więc dość łatwo natknąć się na błędy braku pamięci, nawet jeśli twoja sterta JVM jest chillin około 3 MB, jeśli używasz wielu elementów do rysowania.


6
uruchamianie rysunków z Androidem 3.0 (o strukturze plastra miodu) są przechowywane na stercie
Gu1234

@Timo to czego użyłbyś do wykrywania wycieków w natywnej stercie?
sydd

1
Testowanie, dużo testów. Problem polega na tym, że nie możesz nawet określić, ile pamięci używa Twoja aplikacja, ze względu na współdzielenie pamięci i inne techniki optymalizacji. Odczyty pamięci można uzyskać za pomocą zwykłych poleceń powłoki, ale są to bardzo, bardzo przybliżone szacunki.
Timo Ohr

20

Odpowiedź z @ hp.android działa dobrze, jeśli pracujesz tylko z tłem bitmapowym, ale w moim przypadku miałem BaseAdapterzestaw ImageViews dla a GridView. Zmodyfikowałem unbindDrawables()metodę zgodnie z zaleceniami, aby warunek był następujący:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

ale problem polega na tym, że metoda rekurencyjna nigdy nie przetwarza elementów potomnych AdapterView. Aby rozwiązać ten problem, zamiast tego wykonałem następujące czynności:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

tak, że elementy podrzędne AdapterViewsą nadal przetwarzane - metoda po prostu nie próbuje usunąć wszystkich elementów podrzędnych (co nie jest obsługiwane).

Nie rozwiązuje to jednak problemu, ponieważ ImageViews zarządzają bitmapą, która nie jest ich tłem. Dlatego dodałem, co następuje. Nie jest idealny, ale działa:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

Ogólnie unbindDrawables()metoda jest następująca:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

Mam nadzieję, że istnieje bardziej zasadnicze podejście do uwolnienia takich zasobów.


12

Dobra rozmowa Google I / O (2011) na temat zarządzania pamięcią w systemie Android, a także szczegóły dotyczące narzędzi + technik profilowania pamięci:
http://www.youtube.com/watch?v=_CruQY55HOk



1
OK, więc użytkownik ciągle przełącza się między czynnościami, co oznacza, że ​​mógł zamienić 15 czynności w ciągu 20 sekund. Czy to może być przyczyną błędu braku pamięci? Co mam zrobić, żeby to naprawić? Dzięki!'
Ruchir Baronia


1

Cóż, to są narzędzia, które łączą się z unikalnymi formatami używanymi przez Androida. Myślę, że to, z czego możesz być niezadowolony, to podstawowa struktura kodu testowego w użyciu.

Czy próbowałeś próbować testować obszary kodu przy użyciu Android Mock Framework?


1
nie tyle, testowanie tego rodzaju nie jest tak dużym problemem, jak rejestrowaniem tego, co faktycznie dzieje się podczas działania aplikacji, to, czego naprawdę potrzebuję, to narzędzie do profilowania wycieków zasobów / pamięci
jottos
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.