Donma, bir mobil uygulamanın yavaş ve düzensiz çalıştığı durumu kullanıcının tanımlamasıdır: bazen normal yanıt verir, bazen aniden birkaç saniyeliğine donar. Teknik bağlamda, “donma” sık GC duraklamaları, ana iş parçacığının senkron işlemlerle bloke edilmesi ve optimal olmayan veri yapılarından kaynaklanan lag ve mikro donmaların bir kombinasyonu anlamına gelir. Android Performance Benchmarking Guide'a göre, yanıt süresini 300 ms'den 100 ms'ye düşürmek kullanıcı tutma oranını %25 artırır. Donmayı teşhis etmek, CPU ve Bellek profillemesinin çöp toplama sıklığı analiziyle birleşimini gerektirir.
Önemli noktalar
Donma, kullanıcıların öznel olarak yavaş uygulama performansını tanımlamak için kullandıkları resmi olmayan bir terimdir. Sabit bir gecikme olarak ortaya çıkan lag'ın aksine, donma düzensiz takılmalardan oluşur: uygulama birkaç saniye mükemmel çalışabilir ve ardından aniden 1–3 saniye “düşünebilir”.
Profilleme perspektifinden bakıldığında, donma, tepe gecikmeleri 100 ms'yi aşan bir dizi atlanmış kare (jank) olarak kendini gösterir. Bir FPS grafiğinde bu, keskin düşüşler olarak görünür: saniyede 60 → 20 → 55 → 10 kare. Düşük FPS'li bir lag'ın aksine, donma belirgin bir değişkenliğe sahiptir.
Bir uygulama donduğunda, kullanıcı yavaşlamanın mantığını anlayamaz: ekran pürüzsüz kayabilir ve ardından aniden bir saniyeliğine durabilir. Bu hayal kırıklığına neden olur ve uygulamaya olan güveni azaltır. Google'a göre, kullanıcıların %53'ü yükleme 3 saniyeden uzun sürerse bir siteyi veya uygulamayı terk eder.
Donmanın aralıklı doğası, sorunun sürekli aşırı yükten değil, olay odaklı faktörlerden kaynaklandığını gösterir. Tipik senaryoları inceleyelim.
Android'de, ART çalışma zamanında, çöp toplama tüm uygulama iş parçacıklarını durdurur. Kod çok sayıda geçici nesne oluşturuyorsa — örneğin, her onBindViewHolder çağrısında birleştirme yoluyla yeni bir String oluşturma — GC daha sık çalışır. Bir duraklama, yığın boyutuna ve nesne nesline bağlı olarak 5–50 ms sürebilir. Kullanıcı bunu ani bir “düşünme” olarak algılar.
Android'de Room ve iOS'ta Core Data, asenkron sorguları destekler, ancak geliştiriciler basitlik için genellikle getValue() çağrısı yapar veya runBlocking aracılığıyla sorguları çalıştırır. 10.000 satırlık bir tabloda birleştirmeleri olan ağır bir SELECT 200–500 ms sürebilir ve bu süre zarfında UI'yı tamamen bloke eder.
Bir kamera görüntüsünü (12 MP, 4000x3000 piksel) ölçeklemeden yüklemek, Bitmap kod çözme için 200 ms'ye kadar sürer. Görüntüler asenkron olarak ancak sınırlı bir iş parçacığı havuzu olmadan yükleniyorsa, 5–6 kod çözmenin aynı anda çalıştırılması CPU'yu aşırı yükleyerek göç eden yavaşlamalara neden olabilir.
Aralıklı yavaşlamaları teşhis etmek, sabit lag'ları teşhis etmekten daha zordur çünkü sorun her çalıştırmada tekrarlanmayabilir. Uzun bir süre boyunca istatistik toplanması gerekir.
Android Studio Memory Profiler yalnızca bellek kullanımını değil, aynı zamanda GC olaylarını da gösterir: sıklık, tür (Concurrent, Full), süre. Boşta durumda GC 5 saniyede bir defadan fazla meydana geliyorsa — bu aşırı ayırmanın bir işaretidir. Donma anında bir yığın dökümü almak, hangi nesnelerin belleği işgal ettiğini ortaya çıkarır.
iOS'ta, nesne oluşturma ve serbest bırakmayı izlemek için Instruments'ta Allocations şablonunu kullanın. Generations'ı etkinleştirin — eylemler arasında yığın anlık görüntüleri almanıza ve bellekte hangi nesnelerin kaldığını görmenize olanak tanır. Serbest bırakılmayan kalıcı nesneler, bellek birikiminin ve sonraki duraklamaların kaynağıdır.
JankStats, gerçek zamanlı olarak atlanan kare metriklerini toplayan bir Android kitaplığıdır. Her jank'ı geçerli senaryoya (örneğin, “liste kaydırma”, “ekran açma”) bağlayarak hangi belirli eylemin donmayı tetiklediğini anlamayı sağlar.
Android'de donmaları izlemek için JankStats entegrasyonu örneği:
class MainActivity : AppCompatActivity() {
private lateinit var jankStats: JankStats
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
jankStats = JankStats.create(this.window.decorView) { frameData ->
if (frameData.isJank()) {
Log.w("Jank", "Duration=${frameData.durationMs}ms")
}
}
}
}
Donmayı ortadan kaldırmak, her neden üzerinde hedefe yönelik çalışma gerektirir. Evrensel bir çözüm yoktur — belirli performans profillerinin analizi gerekir.
Bir liste 1000+ öğe içeriyorsa ve tümü aynı anda yükleniyorsa — bu garantili donmadır. Android'de Paging 3 ve iOS'ta NSFetchedResultsController, kullanıcı kaydırdıkça verileri bölümler halinde yükler. Kullanıcı yalnızca ilk 10–20 öğeyi görür; geri kalanı arka planda yüklenir.
Room, Android Studio'daki Denetim Aracı aracılığıyla sorguları profillemeye olanak tanır: yürütme süresi, döndürülen satır sayısı ve sorgu planı görülebilir. WHERE ve ORDER BY sütunlarına indeks eklemek, sorgu süresini 300 ms'den 5 ms'ye düşürebilir. iOS'ta, Instruments'taki Core Data Profiler benzer bir kontrol gerçekleştirir.
Arka plan senkronizasyonları, dosya indirmeleri, veri işleme — tüm bunlar WorkManager (Android) veya Background Tasks (iOS) aracılığıyla yürütülmelidir. Senkronizasyon UI iş parçacığında çalışırsa, uygulama yürütme sırasında donacaktır. WorkManager, pil ve ağ durumunun farkında olarak bir arka plan iş parçacığında yürütmeyi garanti eder.
Android'de WorkManager aracılığıyla arka plan senkronizasyonu örneği:
class SyncWorker(context: Context, params: WorkerParameters)
: CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
Log.d("Sync", "Syncing data in background thread")
syncData()
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
Donma, verimli bellek ve iş parçacığı yönetimi ilkelerini izleyerek kodlama aşamasında önlenebilir.
Baseline Profiles, Android'in JIT yerine önceden (AOT) derlediği sınıflar ve yöntemler listesidir. Profil olmadan, her yeni ekran ilk açılışta derlenir ve 100–500 ms gecikmeye neden olur. Ana ekranlar için bir Baseline Profile hazırlayın ve baseline-profile-gradle-plugin aracılığıyla Gradle'da oluşturmayı etkinleştirin.
Sıcak yol, her karede yürütülen koddur: onBindViewHolder, draw, layoutSubviews. Bu yöntemlerde nesne oluşturmaktan kaçının: nesne havuzları kullanın, birleştirme yerine StringBuilder kullanın, biçimlendirilmiş dizeleri ve biçimlendiricileri önbelleğe alın. Her ekstra ayırma bir sonraki GC'yi yaklaştırır.
CI hattınıza bir liste kaydırma ve ekran açma senaryosuyla Macrobenchmark ekleyin. Bir eşik belirleyin: kare süresinin 99. yüzdelik dilimi 16 ms'yi geçmemelidir. Eşik aşılırsa — optimizasyon yapılana kadar yapı reddedilir.
Sıkça sorulan sorular
Lag, sabit bir gecikmedir (örneğin, her dokunuşta 200 ms). Donma aralıklıdır: uygulama normal çalışır, ardından aniden 1–3 saniye yavaşlar, sonra normale döner. Neden, GC duraklamaları veya senkron veritabanı sorguları gibi olay odaklı faktörlerdir.
Android Studio'da Memory Profiler'ı kullanın: Memory sekmesi GC olaylarını süreyle gösterir. Üretim izleme için, özel izlerle Firebase Performance Monitoring'i entegre edin. iOS'ta Malloc Debug'u etkinleştirin ve Instruments'ta ayırma nesillerini işaretleyin.
Dolaylı olarak — evet. Sunucu yanıtı gecikirse ve UI senkron olarak beklerse, uygulama donar. İstek asenkron olsa bile yanıt işleme UI iş parçacığında yapılırsa — bu da donmaya neden olur. Çözüm, coroutine'ler ve ilerleme göstergeleriyle asenkron işlemedir.
Yanlış kullanıldığında, KMP birlikte çalışabilirlik için aşırı sarmalayıcı nesneler üretebilir. iOS'ta bu, ayırma sıklığını ve dolayısıyla ARC duraklamalarını artırır. @ObjCName kullanın, expect/actual'ı optimize edin ve UI sıcak yollarından paylaşılan koda sık çağrılardan kaçının.
android:largeHeap=”true” aracılığıyla yığını artırmak GC'yi geciktirir ancak ayırmaların nedenini ortadan kaldırmaz. GC sonunda çalıştığında, daha fazla nesnenin geçilmesi gerektiğinden duraklama daha uzun olacaktır. Çözüm, yığını genişletmek değil, ayırma sayısını azaltmaktır.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun