Mobil ilovalarda xotira sizib ketishi — bu nima, sabablari va aniqlash usullari

Muallif: IT Sectr Nashr etilgan: 2026-03-29 O'qish vaqti: 9 daq

Xotira sizib ketishi (Memory Leak) — ilova endi kerak bo'lmagan obyektlarga havolalarni ushlab, axlat yig'uvchiga (GC) occupied memory-ni bo'shatishga to'sqinlik qiladigan holat. LeakCanary ma'lumotlariga ko'ra, hatto yaxshi yozilgan ilovalarda ham 10 000 kod satriga 3–5 sizib ketish uchraydi. Har bir sizib ketish asta-sekin mavjud xotirani kamaytiradi, sekinlashishga va OutOfMemoryError-ga olib keladi.

Asosiy fikrlar

  • Memory Leak — obyekt xotira qoladi, garchi unga ilova mantiqidan faol havolalar bo'lmasa ham
  • Statik havolalar Activity yoki Context-ga — Android-da sizib ketishlarning eng keng tarqalgan sababi
  • LeakCanary — Android-da sizib ketishlarni avtomatik aniqlash uchun standart vosita
  • WeakReference va Application Context — sizib ketishlarning oldini olishning asosiy texnikalari
  • Lifecycle-aware komponentlar obunalar bilan bog'liq sizib ketishlarning butun sinfini yo'q qiladi

Xotira sizib ketishi nima

Xotira sizib ketishi (Memory Leak) — obyekt kuchli havolalar (Strong Reference) zanjiri orqali erishish mumkin bo'lib qoladigan, garchi mantiqan ilova uchun endi kerak bo'lmasa ham. Axlat yig'uvchi (GC) bunday obyektni tirik deb hisoblaydi va u egallagan xotirani bo'shatmaydi. Natijada, mavjud yig'in (Heap) xotirasi doimiy ravishda kamayadi va GC pauzalarining chastotasi ortadi.

Farqli o'laroq qo'lda xotira boshqaruvi bo'lgan tillardan (C, C++), Java/Kotlin-da sizib ketish unutilgan free() emas, balki unutilgan havoladir. Ildiz obyektdan (GC Root) sizib ketayotgan obyektgacha strong reference mavjud ekan, GC uni kerakli deb hisoblaydi. Odatdagi GC Root: statik maydonlar, faol oqimlar, chaqiruv steki, JNI-global havolalar.

Sizib ketishlarning xavfi — ularning yig'ilish effekti. 100 KB lik bitta sizib ketish sezilmaydi, ammo 100 ta bunday sizib ketish 10 MB ni egallaydi va ilova tez-tez GC tufayli sekinlasha boshlaydi. Kritik sizib ketish massasi OutOfMemoryError va ilovaning ishdan chiqishiga olib keladi. Sizib ketish alomatlari: Profiler grafigida xotira iste'molining doimiy o'sishi, STW (Stop The World) bilan tez-tez GC pauzalari va UI unumdorligining pasayishi.

Mobil ilovalarda sizib ketishning odatiy turlari

Besh turdagi sizib ketish mobil ishlanmada 95% hollarni qamrab oladi. Har birining o'z sababi va kodda xarakterli namunasi bor.

Activity yoki Context-ga statik havola

Eng taniqli Android sizib ketishi — Activity yoki Context-ga statik havolani saqlash. Odatdagi kod: onDestroy() vaqtida nolga tushirilmaydigan statik Activity maydoni. Statik maydon tirik ekan, 1–10 MB egallashi mumkin bo'lgan View daraxti bilan butun Activity tirik qoladi. Bu LeakCanary birinchi navbatda topadigan klassik sizib ketishdir.

Yechim: Activity yoki Context-ni hech qachon statik maydonlarda saqlamang. Activity-dan uzoqroq yashovchi singletonlar uchun Application Context dan foydalaning. Activity-ga havola kerak bo'lsa — WeakReference<Activity> dan foydalaning.

kotlin
object MySingleton {
    private var weakActivity: WeakReference<Activity>? = null

    fun attach(activity: Activity) {
        weakActivity = WeakReference(activity)
    }
}

Yashirin havolali ichki sinflar

Anonim sinflar va statik bo'lmagan ichki sinflar yashirin ravishda o'z ichiga olgan sinfga havolani saqlaydi. onDestroy() dan keyin bajariladigan Handler-ga uzatilgan Runnable butun Activity-ni ushlab turadi. Activity-ni yopadigan Retrofit callback ham xuddi shunday qiladi. Bu sizib ketishning eng makkor turi — yashirin havola kodda ko'rinmaydi.

Kotlin object ifodalari va lambdalar ham tashqi sinfga havolalarni tutib oladi. Ichki sinflarni statik (yoki Kotlin-da top-level) qiling va tashqi havolalarni WeakReference orqali uzating. Lambdalar uchun viewLifecycleOwner bilan Lifecycle-aware yondashuvidan foydalaning.

Obunadan chiqilmagan tinglovchilar va obunalar

Obuna tizim xizmatlariga obunadan chiqmasdan — to'g'ridan-to'g'ri sizib ketish. onResume() da ro'yxatdan o'tgan, lekin onPause() da unregister chaqirilmagan SensorManager, LocationManager, NotificationListener Activity-ni ushlab turadi. Xuddi shunday: CompositeDisposable-ga qo'shilmagan RxJava Disposable va GlobalScope orqali ishga tushirilgan korutin.

Lifecycle-aware komponentlardan foydalaning: LifecycleOwner bilan observe() onDestroy() vaqtida avtomatik obunadan chiqadi. RxJava uchun — viewLifecycleOwner.lifecycle.addObserver DisposableObserver bilan. Korutinlar uchun — lifecycleScope.launch() hayotiy tsiklga bog'langan.

kotlin
// Lifecycle orqali avtomatik obunadan chiqish
viewModel.userData.observe(viewLifecycleOwner) { data ->
    updateUI(data)
}

// lifecycleScope bilan korutinlar
lifecycleScope.launch {
    viewModel.loadData().collect { render(it) }
}

Recycle qilinmagan Bitmap

Bitmap sezilarli yig'in xotirasini egallaydi: bitta FullHD-bitmap — 1920 × 1080 × 4 bayt = 8.3 MB. Agar ro'yxatning har bir elementi uchun Bitmap yaratilsa va yashirishda recycle() chaqirilmasa, xotira tez tugaydi. Eski Android versiyalarida (3.0 dan oldin) Bitmap native xotira saqlanardi, ammo zamonaviylarida — Dalvik/ART yig'inida va GC uni faqat strong reference bo'lmaganda bo'shata oladi.

Rasmlarni yuklash uchun Glide yoki Coil dan foydalaning — bu kutubxonalar keshlash va recycle-ni avtomatik boshqaradi. To'g'ridan-to'g'ri Bitmap bilan ishlasangiz, endi ko'rsatilmaydigan katta rasmlar uchun bitmap.recycle() ni chaqiring va kichraytirilgan nusxalarni yuklash uchun inSampleSize dan foydalaning.

onDestroyView dan keyin Fragment havolasi

Fragment ikki hayotiy tsiklga ega: Fragment ning o'zi va uning View si. onDestroyView() dan keyin View daraxti yo'q qilinadi, ammo tashqi havola mavjud bo'lsa, Fragment ning o'zi xotirada qolishi mumkin. Odatdagi xato — yo'q qilinganda tozalanmaydigan ViewPager adapterida yoki navigatsiya grafigida Fragment-ga havolani saqlash.

Hech qachon Fragment-ga havolani uzoq umr ko'ruvchi obyektlarning maydonlarida saqlamang. Ichki fragmentlar uchun childFragmentManager va ular orasida ma'lumot uzatish uchun LifecycleOwner bilan observe() dan foydalaning. ViewPager2 bu muammoni API darajasida hal qildi: FragmentTransactionAdapter hayotiy tsiklni to'g'ri boshqaradi.

Xotira sizib ketishini qanday aniqlash mumkin

Aniqlash ikki faktni tekshirishni talab qiladi: xotira expected lifetime dan keyin qaytmaydi va ma'lum turdagi obyektlar soni kamaymasdan ortadi. Tashxis jarayoni uch bosqichdan iborat.

Birinchi bosqich — Android Studio da Memory Profiler orqali vizual tekshirish. Memory yorlig'ini oching, maqsadli harakatni bajaring (ekranni oching va yoping), GC (Garbage Collection) tugmasini bosing va xotiraning boshlang'ich darajaga qaytganini tekshiring. Agar 3–4 ochish-yopish siklidan keyin xotira barqaror o'ssa — sizib ketish bor.

Ikkinchi bosqich — Heap Dump olish. Memory Profiler da Dump Java Heap tugmasini bosing. Olingan .hprof faylini Android Studio da oching: yig'indagi barcha obyektlarni o'lchamlari va havolalari bilan ko'rasiz. Ekran yopilgandan keyin soni nol bo'lishi kerak bo'lgan sinflarni qidiring. Masalan, yopilgandan keyin soni 2 bo'lgan MainActivity — aniq sizib ketish.

Uchinchi bosqich — Retained Size va GC Root tahlili. Android Studio da Retained Size ni tahlil qiling: ushbu obyektni o'chirish qancha xotirani bo'shatadi. GC Root dan obyektgacha bo'lgan yo'l uni nima ushlab turganini ko'rsatadi: Static field → HashMap → Activity — va siz sizib ketish nuqtasini ko'rasiz. Reference widget paneli obyektning barcha egalarini ko'rsatadi.

Sizib ketishlarni aniqlash vositalari

To'rt vosita sizib ketishlarni aniqlashni avtomatik aniqlashdan chuqur Heap Dump tahliligacha qamrab oladi.

VositaUsulNatija formati
LeakCanaryAvtomatik monitoringHeap Dump + sizib ketish stack trace
Android Memory ProfilerQo'lda monitoringXotira grafigi + Heap Dump
MAT (Eclipse)Chuqur tahlilDominator Tree hisoboti + GC Root yo'li
PerfettoTizim darajasida traceVaqt shkalasi + native xotira

LeakCanary — har bir Android loyihasi uchun must-have. Activity/Fragment hayotiy tsikli tugagandan keyin sizib ketishlarni avtomatik aniqlaydi va sizib ketishning aniq joyini stack trace bilan ko'rsatadi. Integratsiya: build.gradle da bir satr. LeakCanary 2.x qo'lda ishga tushirishni talab qilmaydi — avtomatik ravishda Application Watcher ni ro'yxatdan o'tkazadi.

Xotira sizib ketishining oldini qanday olish mumkin

Profilaktika har bir bosqichda kodni tekshiruvchi qoidalar va vositalar to'plami orqali ishlab chiqish jarayoniga o'rnatiladi.

Kuchli havolalar qoidasi

Hech qachon Activity, Fragment yoki View ga havolani statik maydonda, singletonda yoki uzoq umr ko'ruvchi obyektda saqlamang. Havolasiz iloji bo'lmasa — WeakReference dan foydalaning yoki ma'lumotlarni aynan qancha kerak bo'lsa shuncha yashaydigan va View ni to'g'ridan-to'g'ri saqlamaydigan ViewModel orqali saqlang.

Lifecycle-aware arxitektura

ViewModel va LiveData Android Architecture Components dan hayotiy tsikl muammosini arxitektura darajasida hal qiladi. ViewModel ekran aylanishidan omon qoladi va View ga havolalarni o'z ichiga olmaydi. LiveData onDestroy() vaqtida observer ni avtomatik obunadan chiqaradi. Tizim xizmatlariga qo'lda obuna bo'lish o'rniga ulardan foydalaning.

GC Root ga e'tibor bilan Code Review

Code review da quyidagilarga e'tibor bering: Context/View turidagi statik maydonlar, Activity-ni yopadigan anonim sinflar, lambdalar, qo'lda obunalar, compositesiz RxJava disposable, Bundle orqali Fragment saqlash. Kotlin da qo'shimcha ravishda korutinlarni hayotiy tsiklsiz launch uchun tekshiring.

CI da avtomatik tekshirish

LeakCanary test quvurining bir qismi sifatida ishlashi mumkin: LeakCanary bilan qabul testlarini ishga tushiring va sizib ketish topilsa, build ni muvaffaqiyatsiz deb belgilang. Bu sizib ketishlarning ishlab chiqarishga kirishini oldini oladi. StaticFieldLeak qoidasi bilan Android Lint tekshiruvini to'ldiring — u statik tahlil darajasida potentsial sizib ketishlarni topadi.

kotlin
// Testlarda LeakCanary
class LeakTest {
    @Test
    fun activityShouldNotLeak() {
        ActivityScenario.launch(MainActivity::class.java)
            .close()
        LeakAssertions.assertNoLeak() // sizib ketish bo'lsa fail
    }
}

Tez-tez beriladigan savollar

Xotira sizib ketishi OutOfMemoryError dan qanday farq qiladi?

Sizib ketish sabab, OutOfMemoryError esa natijadir. Bitta sizib ketish OOM ga olib kelmaydi, ammo o'nlab sizib ketishlarning to'planishi Heap ni tugatadi. OOM halokatli istisno, sizib ketish esa vaqt o'tishi bilan unga olib keladigan naqshdir.

LeakCanary siz sizib ketishni qanday topish mumkin?

Android Memory Profiler orqali: ekranni 5 marta oching va yoping, har bir yopishdan keyin GC ni chaqiring. Xotira bazaviy darajaga qaytmasa — sizib ketish bor. Heap Dump oling va ro'yxatda yopilgandan keyin soni 0 dan ko'p bo'lgan Activity sinfini toping.

Kotlin til darajasida sizib ketishlarning oldini ola oladimi?

Qisman. Kotlin null-xavfsizlik muammosini hal qiladi, ammo strong references ni boshqarmaydi. lifecycleScope va viewModelScope bilan korutinlar fon vazifalaridan sizib ketishlarning oldini oladi, sealed class va data class esa sizib ketishga olib keladigan holatlar sonini kamaytiradi. Asosiy himoya — til funksiyalari emas, arxitektura naqshlaridir.

Nima uchun LeakCanary mavjud bo'lmagan sizib ketishni topadi?

LeakCanary ba'zan noto'g'ri ijobiy (false positive) beradi: obyekt vaqtincha tizim tomonidan ushlab turilishi mumkin (masalan, InputMethodManager oxirgi View ni ushlab turadi). Qo'lda tekshiring: agar Retained Size < 1 KB va GC Root tizim xizmati bo'lsa, bu noto'g'ri signal bo'lishi mumkin.

Xotira sizib ketishi faqat Android-da bo'ladimi?

Yo'q. Sizib ketishlar GC bo'lgan har qanday platformada mumkin: iOS (Swift/Objective-C), Flutter (Dart), veb-brauzerlar (JavaScript). Mexanizmlar bir xil — GC Root dan strong reference. iOS da ARC xotirani avtomatik boshqaradi, ammo obyektlar orasidagi retain cycle bir xil sizib ketishni yaratadi.

Xulosa

  • Memory Leak — unutilgan strong reference tufayli GC bo'shata olmaydigan obyekt
  • Statik havolalar Activity va Context ga — sizib ketishlarning eng keng tarqalgan sababi
  • Yashirin havolalar anonim sinflar, lambdalar va RxJava obunalari orqali ochiq havolalardan ko'ra makkorroq
  • LeakCanary sizib ketishlarni avtomatik topadi va aniq stack trace ko'rsatadi
  • Lifecycle-aware komponentlar (ViewModel, LiveData, lifecycleScope) sizib ketishlar sinfini yo'q qiladi
  • Heap Dump va Retained Size tahlili — qo'lda tashxis qo'yishning asosiy usuli
  • Profilaktika strong reference uchun code review va LeakCanary CI tekshiruvini o'z ichiga oladi

Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz

IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.

Loyihani muhokama qilish

Shuningdek o'qing