LeakCanary — co to jest, biblioteka do wyszukiwania wycieków w Androidzie

Autor: IT Sectr Opublikowano: 2026-03-30 Czas czytania: 9 min

LeakCanary — to biblioteka open source od Square do automatycznego wykrywania wycieków pamięci w aplikacjach Android. Integruje się z procesem programowania i w czasie rzeczywistym śledzi cykl życia Activity, Fragment, ViewModel i innych komponentów, sygnalizując wycieki natychmiast po ich wystąpieniu. Według danych Square Open Source, biblioteka jest używana w tysiącach projektów i uważana za standard de facto w diagnostyce pamięci na Androidzie.

Najważniejsze

  • LeakCanary — biblioteka do automatycznego wykrywania wycieków pamięci na Androidzie.
  • Mechanizm działania oparty na WeakReference i ręcznym uruchamianiu GC po zniszczeniu komponentu.
  • Zrzut sterty tworzony automatycznie po wykryciu wycieku i analizowany przez wbudowany analizator.
  • Wynik — dokładny łańcuch referencji (leak trace) wskazujący miejsce wycieku w kodzie.
  • LeakCanary 2.x nie wymaga ręcznej konfiguracji — wystarczy jedna zależność w build.gradle.

Co to jest LeakCanary?

LeakCanary — to biblioteka do automatycznego wykrywania wycieków pamięci w aplikacjach Android, opracowana przez firmę Square. Wbudowuje się w proces budowania aplikacji i automatycznie śledzi, kiedy obiekty, które powinny zostać zniszczone (Activity, Fragment, View), pozostają w pamięci. Po wykryciu wycieku LeakCanary tworzy zrzut sterty i analizuje łańcuch referencji utrzymujących obiekt.

Biblioteka stała się standardem w społeczności Android: według danych GitHub projekt zebrał ponad 28 tysięcy gwiazdek i jest używany w aplikacjach Google, Uber, Airbnb i Facebook. LeakCanary jest dostępny w dwóch głównych wersjach: klasyczna 1.x (z ręczną konfiguracją) i nowoczesna 2.x (automatyczna integracja przez ContentProvider). Wersja 2.x nie wymaga modyfikacji klasy Application — sama zależność wystarczy do pełnego działania.

Głównym zadaniem LeakCanary jest wykrycie sytuacji, gdy obiekt nadal istnieje w pamięci po zakończeniu swojego cyklu życia. Jest to typowe dla wycieków przez pola statyczne, singletonów, niezarejestrowanych callbacków, klas anonimowych i domknięć przechwytujących obiekty zewnętrzne.

Dlaczego LeakCanary jest ważny w programowaniu Android

Wycieki pamięci na Androidzie są bardziej krytyczne niż na desktopie ze względu na ograniczoną ilość RAM w urządzeniach mobilnych. Nawet wyciek 5–10 MB przy każdej zmianie ekranu może doprowadzić do OutOfMemoryError po 30–40 minutach użytkowania aplikacji. LeakCanary wykrywa takie problemy na etapie programowania, nie czekając na awarię na produkcji.

Jak działa LeakCanary?

LeakCanary używa słabych referencji (WeakReference) w połączeniu z wymuszonym wywołaniem garbage collectora. Gdy Activity lub Fragment wywołuje onDestroy, LeakCanary tworzy WeakReference na ten obiekt i uruchamia GC po krótkim opóźnieniu (domyślnie 5 sekund). Jeśli po GC obiekt jest nadal dostępny przez WeakReference, oznacza to, że jest utrzymywany przez silną referencję — wykryto wyciek.

Po wykryciu wycieku LeakCanary wykonuje zrzut sterty — pełny obraz pamięci aplikacji w formacie HPROF. Następnie wbudowany analizator (Shark dla wersji 2.x) buduje graf osiągalności od GC Roots do wyciekającego obiektu i znajduje najkrótszą ścieżkę — łańcuch referencji utrzymujących obiekt w pamięci.

kotlin
// Uproszczona logika detekcji LeakCanary
class ObjectWatcher {
    private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()

    fun watch(watchedObject: Any, description: String) {
        val reference = KeyedWeakReference(watchedObject, description)
        watchedReferences.add(reference)
        BackgroundHandler.postDelayed({
            checkForLeaks()
        }, 5000)
    }

    private fun checkForLeaks() {
        GcTrigger.runGc() // wymuszony GC
        for (ref in watchedReferences) {
            if (ref.get() != null) {
                onLeakFound(ref) // obiekt przeżył GC — to wyciek
            }
        }
    }
}

Kluczowym momentem jest wymuszone wywołanie GcTrigger.runGc(). Bez niego nie można odróżnić obiektu, który rzeczywiście wyciekł, od obiektu, którego GC jeszcze nie zdążył zebrać. LeakCanary robi to do trzech razy: jeśli po trzech cyklach GC obiekt jest nadal w pamięci — wyciek jest potwierdzony.

Czym jest Shark — analizator zrzutów sterty

Shark — to wbudowany w LeakCanary 2.x analizator zrzutów sterty napisany w Kotlinie. W przeciwieństwie do poprzedniego analizatora HAHA, Shark nie ładuje całego pliku HPROF do pamięci, ale przegląda jego graf obiektów z minimalnymi alokacjami. Zmniejsza to zużycie RAM podczas analizy z 50 MB do 2–5 MB i skraca czas analizy z 30 sekund do 1–3 sekund.

Jak zainstalować i skonfigurować LeakCanary?

Instalacja LeakCanary 2.x w nowoczesnym projekcie Android zajmuje jedną linię w build.gradle. Biblioteka używa ContentProvider do automatycznej inicjalizacji — nie trzeba modyfikować klasy Application ani dodawać żadnego kodu w MainActivity. Podłączenie wykonuje się tylko dla debug-build, aby w wydaniowym APK nie było zbędnego kodu.

groovy
// build.gradle (app/module)
dependencies {
    // debugImplementation — biblioteka tylko dla debug-build
    debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}

Po dodaniu zależności i przebudowaniu projektu LeakCanary automatycznie pojawia się w aplikacji. Przy pierwszym uruchomieniu biblioteka pokazuje systemowe powiadomienie o aktywacji. Wszystkie wykryte wycieki są wyświetlane jako powiadomienia — kliknięcie powiadomienia otwiera ekran ze szczegółowym raportem (LeakTrace).

Do personalizacji można stworzyć własny AppWatcherInstaller i nadpisać parametry: timeout oczekiwania na GC, listę śledzonych typów obiektów, włączenie zapisywania zrzutu sterty na dysk. Jednak dla 90% projektów konfiguracja domyślna jest optymalna.

Konfiguracja dla korutyn i Jetpack Compose

Począwszy od wersji 2.12, LeakCanary obsługuje automatyczne śledzenie ViewModel, zakresów korutyn i obiektów State Compose. Dodatkowe zależności nie są wymagane — biblioteka sama określa, które komponenty Jetpack są używane w projekcie, i aktywuje odpowiednie detektory.

Jak czytać raport LeakCanary

Raport LeakCanary (LeakTrace) — to wieloliniowy łańcuch referencji od GC Root do wyciekającego obiektu. Każda linia pokazuje klasę i pole, przez które przechodzi silna referencja. Programista powinien czytać łańcuch od dołu do góry: dolna linia — wyciekający obiekt, górna — punkt wejścia (GC Root).

Typowy LeakTrace wygląda tak: GC Root → statyczne pole Application → singleton → callback → Activity. Jeśli programista widzi taki łańcuch, problem jest jasny: singleton trzyma callback, który przechwycił referencję do Activity. Rozwiązanie — zastąpienie silnej referencji słabą w singletone.

text
┬
├─ android.app.Application
│    Leaking: NO (Application — singleton)
│    ↓ Application.leakedActivities
├─ java.util.ArrayList
│    Leaking: NO (ArrayList — normal)
│    ↓ ArrayList[0]
├─ com.example.MainActivity
│    Leaking: YES (Activity destroyed but still in memory)
│    ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│    Leaking: UNKNOWN
│    ↓ CallbackWrapper.mListener
│              ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│    Leaking: UNKNOWN
│    ↓ MyCallback.this$0
├─ com.example.MainActivity
│    Leaking: YES (MainActivity is the leak)
╰

W tym przykładzie LeakCanary pokazuje, że MainActivity jest utrzymywane przez łańcuch: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → z powrotem MainActivity. Strzałka this$0 wskazuje, że anonimowa klasa MyCallback przechwyciła zewnętrzną referencję do Activity. Rozwiązanie — zrobienie callbacka słabą referencją lub anulowanie go w onDestroy.

LeakCanary pokazuje również stan wycieku dla każdego elementu łańcucha: NO (brak wycieku — to element główny), YES (obiekt powinien być zniszczony), UNKNOWN (nie udało się określić stanu). Status UNKNOWN nie oznacza problemu — to obiekt pośredni, którego LeakCanary nie może jednoznacznie sklasyfikować.

LeakCanary 2.x vs 1.x: kluczowe różnice

Przejście z wersji 1.x na 2.x było radykalne: programiści przepisali bibliotekę od nowa, zastępując przestarzały analizator HAHA własnym silnikiem Shark napisanym w Kotlinie. Shark działa znacznie szybciej, wymaga mniej pamięci do analizy i dokładniej określa główne przyczyny wycieków.

ParametrLeakCanary 1.xLeakCanary 2.x
Język analizatoraJava (HAHA — fork Android SDK)Kotlin (Shark — własny silnik)
InstalacjaRęczna konfiguracja AppWatcher w ApplicationAutomatyczna przez ContentProvider
Szybkość10–30 sekund na analizę zrzutu sterty1–5 sekund na analizę zrzutu sterty
WydajnośćZajmuje 10–50 MB RAM podczas analizyZajmuje 2–10 MB RAM podczas analizy

Kluczową zaletą Shark jest to, że nie ładuje całego zrzutu sterty do pamięci, ale przegląda jego graf referencji z minimalnymi alokacjami. To sprawia, że LeakCanary 2.x nadaje się do użytku na urządzeniach z niską ilością RAM bez ryzyka OutOfMemoryError podczas analizy.

W wersji 2.x pojawiła się również możliwość eksportu zrzutu sterty do pliku do późniejszej analizy w Android Studio Memory Profiler. Aby to zrobić, należy włączyć ustawienie dumpHeapWhenLeakFound w konfiguracji AppWatcher.

Typowe wycieki znajdowane przez LeakCanary

LeakCanary skutecznie wykrywa kilka klas wycieków charakterystycznych dla Androida. Najczęściej występuje wyciek przez statyczne referencje do Activity — programiści przechowują referencję do kontekstu Activity w singletone, a Activity nie może zostać zebrane przez GC po zakończeniu swojego cyklu życia.

Drugą pod względem częstotliwości kategorią są wycieki przez niezarejestrowane słuchacze. Jeśli w onStart został wywołany registerListener, ale w onStop/onDestroy nie wywołano unregisterListener — obiekt-słuchacz jest utrzymywany przez system nawet po zniszczeniu aktywności. LeakCanary jednoznacznie pokazuje, który słuchacz i w jakim serwisie systemowym pozostał aktywny.

kotlin
// Typowy wyciek: Activity przechwycone w callbacku singletona
object AnalyticsManager {
    private var callback: ((String) -> Unit)? = null

    fun register(callback: (String) -> Unit) {
        this.callback = callback // silna referencja na callback
    }

    fun unregister() {
        callback = null // NIE ZAPOMNIJ wywołać w onDestroy!
    }
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        AnalyticsManager.register { event ->
            logEvent(event) // lambda przechwytuje this
        }
        // jeśli nie wywołasz unregister w onDestroy → wyciek Activity
    }
}

Trzecią kategorią są wycieki przez Fragment w BackStack. Jeśli FragmentTransaction.addToBackStack() jest wywoływane bez usuwania Fragmentu przy powrocie, stare instancje Fragmentu pozostają w pamięci. LeakCanary pomaga wykryć takie ukryte wycieki na wczesnych etapach programowania.

Dla każdego wykrytego wycieku LeakCanary oferuje opis i zalecenia dotyczące naprawy. W wersji 2.14 dodano integrację z Android Lint — biblioteka może automatycznie tworzyć zadania w issue tracker po wykryciu wycieku w CI.

Często zadawane pytania

Czy trzeba usuwać LeakCanary z wydaniowego APK?

Tak, koniecznie. LeakCanary jest podłączane przez debugImplementation w build.gradle, co automatycznie wyklucza go z wydaniowej kompilacji. Jeśli podłączysz przez implementation, biblioteka trafi do wydaniowego APK i będzie pokazywać wycieki końcowym użytkownikom — to niedopuszczalne.

Czy LeakCanary spowalnia działanie aplikacji?

Wpływ na wydajność jest minimalny. LeakCanary aktywuje się tylko po onDestroy komponentu i nie ingeruje w renderowanie UI ani obsługę dotyku. Jedynym kosztem jest krótka pauza wymuszonego GC (około 100 ms) i zapis zrzutu sterty przy wycieku (setne części sekundy).

Jak wyeksportować raport LeakCanary?

LeakCanary automatycznie zapisuje zrzut sterty w formacie HPROF w folderze aplikacji. Plik można wyeksportować przez Android Studio: Device File Explorer → data/data/com.example/files/leakcanary/. Aby go wyświetlić, otwórz plik w Memory Profiler przez Capture → Open Heap Dump.

Czy LeakCanary działa z Jetpack Compose?

Tak, od wersji 2.12 LeakCanary w pełni obsługuje Jetpack Compose. Biblioteka śledzi konteksty Composition i obiekty State, automatycznie wykrywając wycieki w funkcjach Composable. Osobna konfiguracja nie jest wymagana — działa od razu.

Czy LeakCanary może się mylić (false positive)?

Fałszywe alarmy są możliwe, ale rzadkie. LeakCanary używa potrójnego wywołania GC przed ogłoszeniem wycieku, co eliminuje większość fałszywych pozytywów. Jeśli uważasz, że alarm jest fałszywy — utwórz IgnoredReference dla konkretnej klasy w konfiguracji.

Podsumowanie

  • LeakCanary — standardowa biblioteka do automatycznego wykrywania wycieków pamięci w aplikacjach Android.
  • Biblioteka używa WeakReference i wymuszonego GC do wykrywania obiektów, które przeżyły swój cykl życia.
  • Zrzut sterty jest analizowany przez wbudowany silnik Shark, który buduje łańcuch referencji od GC Root do wyciekającego obiektu.
  • Instalacja w nowoczesnym projekcie — jedna linia w build.gradle: debugImplementation.
  • LeakCanary 2.x został całkowicie przepisany w Kotlinie i działa 5–10 razy szybciej niż poprzednia wersja.
  • Najczęstsze wycieki: statyczne referencje do Activity, niezarejestrowane słuchacze i Fragment w BackStack.
  • Wdróż LeakCanary w debug-build każdego projektu — zapobiegnie to wyciekom na produkcji.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również