LeakCanary — шта је то, библиотека за проналажење цурења у Android-у

Аутор: IT Sectr Објављено: 2026-03-30 Време читања: 9 мин

LeakCanary — је библиотека отвореног кода од Square за аутоматско откривање цурења меморије у Android апликацијама. Она се интегрише у процес развоја и у реалном времену прати животни циклус Activity, Fragment, ViewModel и других компоненти, сигнализирајући цурења одмах након њиховог настанка. Према подацима Square Open Source, библиотека се користи у хиљадама пројеката и сматра се де-факто стандардом за дијагностику меморије на Android-у.

Главне напомене

  • LeakCanary — библиотека за аутоматско откривање цурења меморије на Android-у.
  • Механизам рада се заснива на WeakReference и ручном покретању GC-ја након уништења компоненте.
  • Heap dump се креира аутоматски при откривању цурења и анализира уграђеним анализатором.
  • Резултат — тачан ланац референци (leak trace) који указује на место цурења у коду.
  • LeakCanary 2.x не захтева ручно подешавање — довољна је једна зависност у build.gradle.

Шта је LeakCanary?

LeakCanary — је библиотека за аутоматско откривање цурења меморије у Android апликацијама, коју је развила компанија Square. Она се уграђује у процес изградње апликације и аутоматски прати када објекти који би требало да буду уништени (Activity, Fragment, View) остају у меморији. При откривању цурења, LeakCanary креира heap dump и анализира ланац референци које држе објекат.

Библиотека је постала стандард у Android заједници: према подацима GitHub-а, пројекат је прикупио преко 28 хиљада звездица и користи се у апликацијама Google, Uber, Airbnb и Facebook. LeakCanary је доступан у две главне верзије: класичној 1.x (са ручним подешавањем) и модерној 2.x (аутоматска интеграција преко ContentProvider-а). Верзија 2.x не захтева измену Application класе — једна зависност је довољна за потпуни рад.

Главни задатак LeakCanary-ја је откривање ситуације када објекат наставља да постоји у меморији након што је његов животни циклус завршен. Ово је типично за цурења преко статичких поља, синглтона, нерегистрованих колбекова, анонимних класа и затварања која хватају спољашње објекте.

Зашто је LeakCanary важан за Android развој

Цурења меморије на Android-у су критичнија него на десктопу због ограничене количине RAM-а на мобилним уређајима. Чак и цурење од 5–10 MB при свакој промени екрана може довести до OutOfMemoryError након 30–40 минута коришћења апликације. LeakCanary открива такве проблеме у фази развоја, не чекајући пад на продукцији.

Како ради LeakCanary?

LeakCanary користи слабе референце (WeakReference) у комбинацији са присилним позивом garbage collector-а. Када Activity или Fragment позове onDestroy, LeakCanary креира WeakReference на тај објекат и покреће GC након кратког кашњења (подразумевано 5 секунди). Ако након GC-ја објекат још увек може да се достигне преко WeakReference-а, значи да га држи јака референца — евидентира се цурење.

Након откривања цурења, LeakCanary прави heap dump (потпуни снимак меморије апликације у HPROF формату). Затим уграђени анализатор (Shark за верзију 2.x) гради граф достижности од GC Roots-а до процурелог објекта и проналази најкраћи пут — ланац референци које држе објекат у меморији.

kotlin
// Поједностављена логика детекције 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() // присилни GC
        for (ref in watchedReferences) {
            if (ref.get() != null) {
                onLeakFound(ref) // објекат је преживео GC — ово је цурење
            }
        }
    }
}

Кључни моменат — присилни позив GcTrigger.runGc(). Без њега није могуће разликовати објекат који је стварно процурео од објекта који GC још није стигао да сакупи. LeakCanary ово ради до три пута: ако након три GC циклуса објекат још увек постоји у меморији — цурење је потврђено.

Шта је Shark — анализатор heap dump-а

Shark — је уграђени анализатор heap dump-а у LeakCanary 2.x, написан у Kotlin-у. За разлику од претходног анализатора HAHA, Shark не учитава цео HPROF фајл у меморију, већ обилази његов граф објеката са минималним алокацијама. Ово смањује потрошњу RAM-а током анализе са 50 MB на 2–5 MB и скраћује време анализе са 30 секунди на 1–3 секунде.

Како инсталирати и подесити LeakCanary?

Инсталација LeakCanary 2.x у модеран Android пројекат траје један ред у build.gradle. Библиотека користи ContentProvider за аутоматску иницијализацију — није потребно мењати Application класу или додавати код у MainActivity. Повезивање се врши само за debug-build, како у release APK-у не би било вишка кода.

groovy
// build.gradle (app/module)
dependencies {
    // debugImplementation — библиотека само за debug-build
    debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}

Након додавања зависности и поновне изградње пројекта, LeakCanary се аутоматски појављује у апликацији. При првом покретању, библиотека приказује системско обавештење о активацији. Сва откривена цурења се приказују као обавештења — клик на обавештење отвара екран са детаљним извештајем (LeakTrace).

За прилагођавање можете креирати сопствени AppWatcherInstaller и преиначити параметре: timeout чекања GC-ја, листу праћених типова објеката, укључивање чувања heap dump-а на диск. Међутим, за 90% пројеката подразумевана конфигурација је оптимална.

Подешавање за корутине и Jetpack Compose

Почевши од верзије 2.12, LeakCanary подржава аутоматско праћење ViewModel-а, корутинских опсега и State објеката Compose-а. Додатне зависности нису потребне — библиотека сама одређује које Jetpack компоненте се користе у пројекту и активира одговарајуће детекторе.

Како читати извештај LeakCanary-ја

Извештај LeakCanary-ја (LeakTrace) — је вишередни ланац референци од GC Root-а до процурелог објекта. Сваки ред показује класу и поље кроз које пролази јака референца. Програмер треба да чита ланац одоздо нагоре: доњи ред — процурели објекат, горњи — улазна тачка (GC Root).

Типичан LeakTrace изгледа овако: GC Root → статичко поље Application → синглтон → колбек → Activity. Ако програмер види такав ланац, проблем је јасан: синглтон држи колбек који је ухватио референцу на Activity. Решење — заменити јаку референцу слабом у синглтону.

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)
╰

У овом примеру LeakCanary показује да се MainActivity држи кроз ланац: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → поново MainActivity. Стрелица this$0 указује да је анонимна класа MyCallback ухватила спољашњу референцу на Activity. Решење — направити колбек слабом референцом или га отказати у onDestroy-ју.

LeakCanary такође приказује статус цурења за сваки елемент ланца: NO (нема цурења — ово је коренски елемент), YES (објекат треба да буде уништен), UNKNOWN (није могуће одредити статус). Статус UNKNOWN не значи проблем — то је међуобјекат који LeakCanary не може недвосмислено класификовати.

LeakCanary 2.x наспрам 1.x: кључне разлике

Прелазак са верзије 1.x на 2.x био је радикалан: програмери су преписали библиотеку из темеља, заменивши застарели HAHA анализатор сопственим погоном Shark написаним у Kotlin-у. Shark ради знатно брже, захтева мање меморије за анализу и прецизније одређује основне узроке цурења.

ПараметарLeakCanary 1.xLeakCanary 2.x
Језик анализатораJava (HAHA — fork Android SDK-ја)Kotlin (Shark — сопствени погон)
ИнсталацијаРучно подешавање AppWatcher-а у Application-уАутоматска преко ContentProvider-а
Брзина10–30 секунди за анализу heap dump-а1–5 секунди за анализу heap dump-а
ПерформансеЗаузима 10–50 MB RAM-а током анализеЗаузима 2–10 MB RAM-а током анализе

Кључна предност Shark-а — не учитава цео heap dump у меморију, већ обилази његов граф референци са минималним алокацијама. Ово чини LeakCanary 2.x погодним за употребу на уређајима са малом количином RAM-а без ризика од OutOfMemoryError-а током анализе.

У верзији 2.x такође се појавила могућност извоза heap dump-а у фајл за каснију анализу у Android Studio Memory Profiler-у. За то је потребно укључити поставку dumpHeapWhenLeakFound у конфигурацији AppWatcher-а.

Типична цурења које LeakCanary проналази

LeakCanary ефикасно открива неколико класа цурења карактеристичних за Android. Најчешће се јавља цурење преко статичких референци на Activity — програмери чувају референцу на контекст Activity-ја у синглтону, и Activity не може да буде сакупљена од стране GC-ја након завршетка свог животног циклуса.

Друга категорија по учесталости — цурења преко нерегистрованих слушалаца. Ако је у onStart-у позван registerListener, али у onStop/onDestroy није позван unregisterListener — објекат-слушалац држи систем чак и након уништења активности. LeakCanary недвосмислено показује који слушалац и у којој системској услузи је остао активан.

kotlin
// Типично цурење: Activity захваћена у колбеку синглтона
object AnalyticsManager {
    private var callback: ((String) -> Unit)? = null

    fun register(callback: (String) -> Unit) {
        this.callback = callback // јака референца на колбек
    }

    fun unregister() {
        callback = null // НЕ ЗАБОРАВИТЕ да позовете у onDestroy!
    }
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        AnalyticsManager.register { event ->
            logEvent(event) // ламбда хвата this
        }
        // ако не позовете unregister у onDestroy → цурење Activity
    }
}

Трећа категорија — цурења преко Fragment-а у BackStack-у. Ако се FragmentTransaction.addToBackStack() позива без уклањања Fragment-а при повратку, старе инстанце Fragment-а остају у меморији. LeakCanary помаже да се открију таква скривена цурења у раним фазама развоја.

За свако откривено цурење, LeakCanary нуди опис и препоруке за исправку. У верзији 2.14 додата је интеграција са Android Lint-ом — библиотека може аутоматски да креира задатке у issue tracker-у при откривању цурења у CI-ју.

Често постављана питања

Да ли треба уклонити LeakCanary из release APK-а?

Да, обавезно. LeakCanary се повезује преко debugImplementation у build.gradle, што га аутоматски искључује из релизне верзије. Ако га повежете преко implementation, библиотека ће доспети у release APK и приказивати цурења крајњим корисницима — то је неприхватљиво.

Да ли LeakCanary успорава рад апликације?

Утицај на перформансе је минималан. LeakCanary се активира само након onDestroy компоненте и не утиче на приказивање UI-ја или обраду додира. Једини трошак — кратка пауза присилног GC-ја (око 100 ms) и запис heap dump-а при цурењу (стоти део секунде).

Како извести извештај LeakCanary-ја?

LeakCanary аутоматски чува heap dump у HPROF формату у фасцикли апликације. Фајл се може извести преко Android Studio: Device File Explorer → data/data/com.example/files/leakcanary/. За преглед отворите фајл у Memory Profiler-у преко Capture → Open Heap Dump.

Да ли LeakCanary ради са Jetpack Compose-ом?

Да, почевши од верзије 2.12 LeakCanary у потпуности подржава Jetpack Compose. Библиотека прати Composition контексте и State објекте, аутоматски откривајући цурења у Composable функцијама. Посебно подешавање није потребно — ради одмах.

Може ли LeakCanary да погреши (false positive)?

Лажна активирања су могућа, али ретка. LeakCanary користи троструки позив GC-ја пре проглашења цурења, што елиминише већину лажних позитивних резултата. Ако сматрате да је активирање лажно — креирајте IgnoredReference за одређену класу у конфигурацији.

Закључак

  • LeakCanary — стандардна библиотека за аутоматско откривање цурења меморије у Android апликацијама.
  • Библиотека користи WeakReference и присилни GC за откривање објеката који су надживели свој животни циклус.
  • Heap dump анализира уграђени погон Shark, који гради ланац референци од GC Root-а до процурелог објекта.
  • Инсталација у савременом пројекту — један ред у build.gradle: debugImplementation.
  • LeakCanary 2.x је у потпуности преписан у Kotlin-у и ради 5–10 пута брже од претходне верзије.
  • Најчешћа цурења: статичке референце на Activity, нерегистровани слушаоци и Fragment у BackStack-у.
  • Уведите LeakCanary у debug-build сваког пројекта — ово ће спречити цурења на продукцији.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође