Yaddaş sızması — mobil inkişafda ən gizli problemlərdən biridir. Tətbiqin yaddaşı durmadan artır, nəhayət ƏS tərəfindən təyin edilmiş həddə çatana qədər, bundan sonra OutOfMemoryError və ya məcburi dayandırma baş verir. Square Engineering məlumatlarına görə, təxminən 40% Android tətbiqlərində yalnız profilləmə zamanı aşkar edilə bilən ən azı bir yaddaş sızması var. Yaddaş artımının səbəblərini və qarşısının alınması üsullarını nəzərdən keçirək.
Əsas məqamlar
Yaddaş sızması (memory leak) — tətbiqə artıq lazım olmayan bir obyektin, kök dəstindən (GC Root) aktiv istinad olduğu üçün yaddaşda qalmağa davam etdiyi vəziyyət. Zibil toplayıcı belə bir obyekti canlı hesab edir və onu silmir.
Yaddaşın şişməsi (memory bloat) — tətbiqin cari tapşırıqları yerinə yetirmək üçün lazım olduğundan daha çox yaddaş istehlak etdiyi daha geniş bir problemdir. Səbəblər: həddindən artıq keşləmə, obyektlərin təkrarlanması, optimal olmayan məlumat strukturları və yaddaş parçalanması.
Android tətbiqləri üçün məhdud yaddaş ayrılır (adətən cihazdan və ƏS versiyasından asılı olaraq 64-512 MB). iOS məhdudiyyəti daha az sərtdir, lakin sistem limitə yaxınlaşdıqda xəbərdarlıq göndərir.
| Xüsusiyyət | Android | iOS |
|---|---|---|
| Yaddaş limiti | 64-512 MB (cihazdan asılıdır) | Qeyri-aşkar (sistem) |
| Zibil toplanması | ART (Concurrent, Compact) | ARC (Automatic Reference Counting) |
| Sızma mexanizmi | GC Root istinadları | Retain cycles (güclü istinad dövrələri) |
| Nəticə | OutOfMemoryError | Yaddaş xəbərdarlığı → dayandırma |
Facebook Engineering Blog məlumatlarına görə, yaddaş sızmaları mobil tətbiqlərdə crash hesabatlarının təxminən ~15%-nin səbəbidir. Androiddə buna yaddaş çatışmazlığı zamanı tez-tez GC fasilələri səbəbindən ANR da əlavə olunur.
Activity-ə statik istinad — Android sızmalarının klassikası. Statik sahə və ya sinqlton Activity-yə istinad saxlayırsa, sinqlton canlı olduğu müddətcə finish()-dən sonra belə GC tərəfindən toplanmayacaq. Activity View hierarchy, resurslar və Context-i ehtiva edən ağır bir obyektdir.
object LeakHolder {
var activityRef: Activity ?= null // sizma: Activity-yə statik istinad
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle ?= null) {
super.onCreate(savedInstanceState)
LeakHolder.activityRef = this // ❌ MainActivity heç vaxt GC tərəfindən toplanmayacaq
}
}
Anonim siniflər və lambdalar — gizli şəkildə xarici sinfə istinad saxlayır. Runnable və ya Callback xarici xidmətə ötürülərsə və Activity məhv edilərsə, anonim sinif obyekti hələ də növbədə qalır və Activity-nin zibil toplanmasına getməsinə mane olur.
iOS-da əsas problem retain cycles-dır: iki obyekt bir-birinə güclü istinadlar saxlayır və ARC heç biri üçün istinad sayını sıfırlaya bilmir. Tipik hal: self-i güclü şəkildə tutan closure və self-in closure-a istinad saxlaması.
LeakCanary — Androiddə avtomatik sızma aşkarlanması üçün Square kitabxanası. Activity və ya Fragment məhv edildikdən sonra obyektin GC tərəfindən toplanıb-toplanmadığını yoxlayır. Əgər yoxsa — heap dump edir və sızma trace-ni göstərir.
// LeakCanary 2.x — Application vasitəsilə avto-inteqrasiya
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// LeakCanary debug qurmasında avtomatik quraşdırılır
// ContentProvider vasitəsilə — sıfır kod quraşdırması
}
}
// Məcburi yoxlama çağırışı
AppWatcher.objectWatcher.watch(watchedObject, "leak description")
Android Studio Profiler — real vaxtda yaddaşı izləmək üçün quraşdırılmış alət. Heap dump yazmağa, şübhəli obyektləri (Retained Size > 1 MB) tapmağa və hər bir obyektə qədər GC root yolunu izləməyə imkan verir.
iOS üçün Xcode Memory Graph Debugger istifadə edin. O, yaddaşdakı obyektlərin qrafikini vizuallaşdırır, retain cycles göstərir və dairəvi istinadları dərhal aşkar etməyə imkan verir. Uzunmüddətli izləmə üçün Instruments > Allocations da mövcuddur.
WeakReference — zibil toplanmasına mane olmamalı olan istinadlar üçün əsas mexanizm. GC obyekti silmək qərarına gələrsə, WeakReference null qaytarır. Geri çağırışlar, dinləyicilər və fon iplərindən UI komponentlərinə istinadlar üçün istifadə olunur.
Lifecycle-aware komponentləri — Android Jetpack-də (Lifecycle, LiveData, Flow, coroutines) tətbiq edilmiş memarlıq yanaşması. Abunəliklər onDestroy zamanı avtomatik ləğv edilir ki, bu da əsas sızma sinfini aradan qaldırır.
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<List<User>>()
val data: LiveData<List<User>> get() = _data
fun loadData() {
viewModelScope.launch {
val result = repository.fetchData()
_data.postValue(result)
// korutina onCleared() zamanı avtomatik ləğv olunur
}
}
}
viewModelScope və lifecycleScope — Android-də həyat dövrü hadisəsi zamanı ləğv edilən quraşdırılmış CoroutineScope-lardır. Bu, müasir Android inkişafında ən çox yayılmış ssenari olan korutinlar vasitəsilə sızmaları aradan qaldırır.
Memory Profiler in Android Studio — yaddaşı izləmək üçün əsas alət. Canlı ayırmaları, yaddaş şəkillərini, növlərə görə obyektlərin sayını göstərir. Dump yazmağa və şübhəli obyektləri tapmaq üçün MAT (Memory Analyzer Tool) ilə təhlil etməyə imkan verir.
Eclipse MAT — yaddaş dump-ları üçün masaüstü analizator. Android Studio-dan HPROF faylı yükləndikdən sonra MAT dominator ağacı qurur, hər bir obyektin retain ölçüsünü göstərir və Leak Suspects Report vasitəsilə şübhəli sızmaların avtomatik təhlilini təklif edir.
Xcode Memory Graph — retain cycles üçün vizual debugger. Memory Graph Debugger düyməsini kliklədikdə Xcode tətbiqi dayandırır, yaddaşdakı obyektlərin tam qrafikini qurur və retain cycles qırmızı rənglə vurğulayır.
| Alət | Platforma | Xüsusiyyət |
|---|---|---|
| LeakCanary | Android | Məhv edildikdən sonra avtomatik sızma aşkarlanması |
| Memory Profiler | Android Studio | Heap dump + canlı ayırmalar |
| Eclipse MAT | Android | Dominator ağacı, Leak Suspects Report |
| Memory Graph | iOS (Xcode) | Retain cycles vizuallaşdırıcısı |
Google I/O 2023 məlumatlarına görə, debug qurmalarında LeakCanary istifadə edən tətbiqlər tətbiqdən sonra ilk 2 ay ərzində yaddaşla bağlı crash-ların sayını 30-50% azaldır. LeakCanary-nin layihəyə onboarding mərhələsində əlavə edilməsi tövsiyə olunur.
Tez-tez verilən suallar
Sızma — kod üçün əlçatmaz olan, lakin aktiv istinadlar səbəbindən GC tərəfindən silinməyən obyektlər. Şişmə — tətbiqin məntiqi olaraq lazım olan, lakin həddindən artıq miqdarda obyektləri yaddaşda saxlaması (məsələn, 80 MB ağırlığında işləyən tətbiqdə 50 MB keş). Şişmə memarlıq yolu ilə müalicə olunur, sızma isə istinadların düzgün idarə edilməsi ilə.
LeakCanary ObjectWatcher istifadə edir — onDestroy() Activity-dən sonra Activity-yə WeakReference yaradır və GC işə salır. Əgər 5 saniyədən sonra WeakReference təmizlənməzsə, LeakCanary heap dump edir, GC Root-dan obyektə qədər ən qısa istinad zəncirini təhlil edir və fayl adı və kod sətri ilə dəqiq sızma stackini göstərir.
Bitmap Java yaddaşından kənarda, native yaddaşda (native heap) yer tutur. Bir Bitmap-in ölçüsü = en × hündürlük × 4 bayt (ARGB_8888). 12 MP foto (4000×3000) 48 MB yer tutur. Android native yaddaşı vaxtında boşalda bilməz, bu da bir neçə Bitmap yığıldıqda kifayət qədər Java yaddaşı olsa belə OOM-a səbəb olur.
Retain cycle — ARC-də iki obyektin bir-birinə güclü istinadlar saxladığı və istinad sayının heç vaxt sıfıra çatmadığı vəziyyət. Tipik nümunə: ViewController-in closure-a güclü istinadı və closure-un self-i güclü tutması. Həll: closure-larda [weak self] və ya [unowned self] istifadə edin.
Yaddaş ölçüsü cihazdan və Android versiyasından asılıdır. Köhnə cihazlar üçün (API 15-24) — 64-128 MB. Müasir cihazlar üçün (API 25+) — 256-512 MB. Dəqiq dəyəri ActivityManager.getMemoryClass() vasitəsilə əldə etmək olar. Böyük tətbiqlər (oyunlar, redaktorlar) üçün manifestdə largeHeap=true var, 1 GB-a qədər verir.
Xülasə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun