WebView, Android uygulamanızda web içeriğini oluşturmak için birden fazla işlemde yerel kod çalıştırır. WebView örneklerinin yönetilmeden bırakılması bellek sızıntılarına, bellek yetersizliği (OOM) kilitlenmelerine ve uygulama performansının düşmesine neden olabilir.
Bu belgede, WebView çoklu işlem bellek modeli açıklanmakta, sızıntıları önlemek için yaşam döngüsünün nasıl doğru şekilde yönetileceği anlatılmakta ve bellek sorunlarını teşhis etmek için pratik iş akışları sunulmaktadır.
WebView bellek mimarisini anlama
WebView belleğini etkili bir şekilde yönetmek için Android'in web içeriği için kaynakları nasıl tahsis ettiğini anlamanız gerekir:
Çoklu işlem yürütme: Android 8.0 (API düzeyi 26) ve sonraki sürümlerde
WebView, web içeriğini uygulamanızın temel işlevlerinden birden fazla işlemle ayırır (düşük RAM'li cihazlarda tek bir işleme geri dönebilir):- Ana makine (tarayıcı) işlemi:
Activityve Java veya Kotlin kodunuzun çalıştığı ana uygulama işlemi. - Yalıtılmış oluşturucu işlemi: HTML ve CSS'yi ayrıştıran, JavaScript'i yürüten ve web sayfalarını oluşturan ayrı bir korumalı alan işlemi (
SandboxedProcessService).
- Ana makine (tarayıcı) işlemi:
Yerel bellekte kaplanan yer: Oluşturulan grafikler, DOM ağacı ve JavaScript çalışma zamanı belleği de dahil olmak üzere çoğu
WebViewbelleği, Java yığınında değil yerel bellekte ayrılır. Java yığın dökümü (.hprof) yalnızca basit bir Java sarmalayıcı nesnesi gösterir ve web içeriği tarafından kullanılan gerçek belleği yakalamaz.Yerel belleğin sistem üzerindeki etkisi: Uygulamanın
maxHeapsınırı ile sınırlanan veOutOfMemoryErrorile hızlı bir şekilde başarısız olan Java yığın ayırmalarının aksine, yerel bellek sessizce gigabaytlara kadar büyüyebilir. Serbest bırakılmayan yerel bellek, fiziksel RAM'i ve takas alanını (zRAM) doldurduğunda Android'in Low Memory Killer (LMK) özelliği, belleği geri kazanmak için arka plan işlemlerini sonlandırmaya başlar. Bu durum, ön planda çalışan uygulamayı sonlandırmadan önce cihazın çoklu görev performansını düşürür.
WebView yaşam döngüsünü yönetme
Bellek sızıntılarını önlemek için yaşam döngüsünün doğru şekilde yönetilmesi çok önemlidir. Sık yapılan bir hata, bir WebView öğesini düzeninizden kaldırmanın veya bir Activity öğesinin otomatik olarak bitmesine izin vermenin belleğini boşalttığını varsaymaktır.
Hem Java bağlam referanslarının hem de yerel oluşturma kaynaklarının tamamen temizlenmesini sağlamak için ana bileşeninizin yaşam döngüsünde (ör. onDestroy()) açıkça bir kaldırma sırası düzenlemeniz, etkin sayfa yürütmeyi durdurmanız, görünümü kapsayıcısından ayırmanız ve yerel bağlamaları serbest bırakmanız gerekir.
WebView örneklerini temizleme
Activity veya Fragment öğeniz imha edildiğinde temiz bir kapatma işlemi gerçekleştirmek ve kaynakları serbest bırakmak için aşağıdakileri yapın:
WebViewöğesini üst kapsayıcısından (ViewGroup) kaldırın.- Etkin yüklemeyi durdurur ve gezinme geçmişini temizler.
- Şu numaraya telefon et:
destroy(). nullreferansını temizleyin.
Aşağıdaki örnekte, WebView öğesinin nasıl düzgün bir şekilde temizleneceği gösterilmektedir:
Kotlin
override fun onDestroy() { myWebView?.let { // Remove the WebView from its parent ViewGroup. (it.parent as? ViewGroup)?.removeView(it) // Stop active loading and clear history. it.stopLoading() it.clearHistory() // Destroy the instance. it.destroy() } myWebView = null super.onDestroy() }
Java
@Override
protected void onDestroy() {
if (myWebView != null) {
// Remove the WebView from its parent ViewGroup.
if (myWebView.getParent() instanceof ViewGroup) {
((ViewGroup) myWebView.getParent()).removeView(myWebView);
}
// Stop active loading and clear history.
myWebView.stopLoading();
myWebView.clearHistory();
// Destroy the instance.
myWebView.destroy();
}
myWebView = null;
super.onDestroy();
}
İmha sonrası bellek hakkında bilgi
destroy() yöntemini çağırdığınızda sistem, Activity bağlamını serbest bırakır, görünüm hiyerarşilerini temizler ve web arka plan çalışmasını durdurur. Ancak işlemin fiziksel belleğinin (Yerleşik Küme Boyutu) hemen WebView öncesi referans değerine düşmediğini görebilirsiniz.
Bu normal bir durumdur. Yerel çalışma zamanı önbellekleri, paylaşılan kitaplıklar ve ayrılan bellek sayfaları, işletim sistemi bunları geri alana veya işlem sonlandırılana kadar işlemde kalır. destroy() özelliğinin temel amacı, kullanıcılar web destekli ekranlara girip çıkarken kümülatif Activity bellek sızıntılarını önlemektir.
Temel hata ayıklama metrikleri
WebView bellek tüketimini analiz ederken aşağıdaki metriklere odaklanın:
Yerleşik Küme Boyutu (RSS): Paylaşılan kod ve kitaplıklar dahil olmak üzere işleme eşlenen toplam fiziksel RAM (Android Studio Profiler'da Toplam olarak etiketlenir).
Anonim RSS (RssAnon): Doğrudan işlem tarafından ayrılan ve diskteki bir dosya tarafından desteklenmeyen bellek (ör. yerel yığın ve JavaScript çalışma zamanı bellek ayırmaları). Bu, web içeriğinizin birincil bellek maliyetini temsil eder (Android Studio Profiler'da Ayrılmış olarak etiketlenir).
Özel Bellekte Kaplanan Yer (PMF): Anonim RSS ve takasın (zRAM) toplamı. PMF, uygulamanızın sistem üzerinde oluşturduğu gerçek tahliye edilemeyen bellek yükünü yansıtır.
Tarayıcı PMF'si ile oluşturucu PMF'si: Uygulamanızın ana işlemi tarafından kullanılan bellek ile izole edilmiş oluşturucu işlemi tarafından kullanılan bellek. Ağır web içeriği, öncelikle oluşturma işleminde ani artışlara neden olur.
Canlı Nesne Sayıları (
WebViews,Activities,Views): Bellekte tutulan etkin kullanıcı arayüzü, bağlam veWebViewörneklerinin sayısı. Bunları izleyerek bellek artışının, tutulan Java referanslarından mı yoksa yalnızca yerel ayırmalardan mı kaynaklandığını belirleyebilirsiniz.Özel Diğer ve Yerel Yığın:
dumpsys meminfosürümünde yerel C/C++ ayırmaları ve özel bellek eşlemeleri (ör. ChromiumPartitionAllocveya yerleşik JavaScript çalışma zamanı yığınları), Java Yığını yerine Yerel Yığın ve Özel Diğer bölümünde görünür.
İşlem belleği sayaçları ve kategorileri hakkında daha fazla bilgi için İşlem belleği sözlüğü'ne bakın.
Pratik teşhis iş akışları
WebView, birden fazla işlemde çalıştığı ve yerel bellek ayırdığı için ayak izini incelemek üzere aşağıdaki araçları ve teknikleri kullanın:
Profil oluşturma ve teşhis araçları
Bellek ayırmaları incelemek ve sızıntıları teşhis etmek için aşağıdaki araçları kullanın:
Android Studio Memory Profiler: Yerel ayırmaları görselleştirmek, bellek kategorilerini zaman içinde izlemek ve ekran geçişleri sırasında sızıntıları tespit etmek için Memory Profiler'ı kullanın.
ActivityPerfetto ile bellek izleme: Genel bellek artışını gözlemlemek için sistem düzeyindeki bellek sayaçlarını (ör. RSS ve anonim RSS) kaydetmek üzere Perfetto'yu kullanın.
WebViewyerel motor ayırmalarının Perfetto'nun öbek profili oluşturma aracında çağrı yığınları oluşturmadığını unutmayın. Web içeriğindeki JavaScript yığın anlık görüntülerini ve DOM ayırmalarını incelemek için Chrome Geliştirici Araçları'nı kullanın.
Canlı nesne sayılarını inceleme
Bellek artışının, tutulan Java çerçeve nesnelerinden (ör. kullanıcı arayüzü bileşenleri) mi yoksa yerel ayırmalardan mı kaynaklandığını belirlemek için dumpsys meminfo'nin Objects bölümünü inceleyin:
adb shell dumpsys meminfo -a <var>PACKAGE_NAME</var> | grep -A 10 "Objects"Çıkışta canlı nesne sayıları gösterilir:
Objects
Views: 142 ViewRootImpl: 1
AppContexts: 3 Activities: 1
Assets: 12 AssetManagers: 0
Local Binders: 32 Proxy Binders: 45
Parcel memory: 15 Parcel count: 30
Death Recipients: 2 WebViews: 1
Bu bölümde etkin çerçeve nesnelerinin, IPC tanıtıcılarının ve paket ayırmalarının sayısı gösterilir. WebView teşhisi için öncelikle Activities ve WebViews değerlerine odaklanın.
Hedef kullanıcı etkileşimini (ör. bir web ekranını açıp kapatma) tekrar tekrar gerçekleştirin ve sayıları karşılaştırın:
Örnek sızıntısı: Her gezinmede
WebViewsveyaActivitiesartıyor ve referans değerine dönmüyorsa uygulamanız JavaWebViewörneğini veya ana makineActivityörneğini sızdırıyordur (örneğin, eksik birViewGroup.removeView()veya saklanan dinleyici referansları nedeniyle). Sızdırılan birActivity, tüm görünüm ağacını ve kodu çözülmüş resim kaynaklarını belleğe sabitlediğinden, tekrarlanan ziyaretler Java yığınını hızla tüketir veOutOfMemoryErrorkilitlenmelerine neden olur.Yerel veya DOM sızıntısı: Toplam işlem RSS'si ve Özel Diğer artmaya devam ederken
WebViewsveActivitiessabit kalırsa sızıntı, yayınlanmamış yerel kaynaklardan, DOM öğelerinden veya JavaScript motoru bağlamalarından kaynaklanır. Bu tahsisler yerel bellekte bulunduğundan ve ART çöp toplayıcısını atladığından, standart Java sızıntı algılama araçları tarafından görünmez ve işletim sistemi uygulamayı sonlandırana kadar birikmeye devam eder.
KSA'yı kullanarak yalıtılmış oluşturucu işleminin profilini oluşturma
Uygulamanızın paket adıyla dumpsys meminfo komutunu çalıştırdığınızda yalnızca ana ana makine işleminin belleği gösterilir. Web sayfalarının oluşturulduğu yalıtılmış oluşturucu işlemini incelemek için:
Yalıtılmış oluşturucu hizmetinin işlem kimliğini (PID) bulun:
adb shell dumpsys activity processes <var>PACKAGE_NAME</var> | grep "Isolated.*SandboxedProcessService"Çıktıda, izole edilmiş işlem kaydı ve PID'si RENDERER_PID (ör.
22155) gösterilir:Isolated #5: ProcessRecord{... 22155:com.google.android.webview.debug:sandboxed_process0:...}PID'sini kullanarak oluşturucu işleminin bellek dökümünü inceleyin:
adb shell dumpsys meminfo <var>RENDERER_PID</var>Tarayıcı tarafındaki ayak izini değerlendirmek için ana uygulama işlemini inceleyin:
adb shell dumpsys meminfo <var>PACKAGE_NAME</var>
Bellek haritalarını ve ayırmalarını inceleme
Anonim belleği hangi yerel alt sistemlerin veya ayırıcıların kullandığını görmek için işlem bellek haritalarını inceleyin:
adb shell "cat /proc/$(pidof <var>PACKAGE_NAME</var>)/maps" | grep "anon:"Aşağıdaki tabloda, yaygın anonim bellek etiketleri ve bunların bellek artışıyla ilişkisi listelenmiştir:
| Bellek Etiketi | Alt sistem | Uygulama ve web içeriğiyle alaka düzeyi | Bellek artışının yaygın nedeni nedir? |
|---|---|---|---|
[anon:partition_alloc] |
Chromium PartitionAlloc | WebView uygulamasında DOM ağaçları, oluşturma arabellekleri, V8 JavaScript yığını ve WebAssembly yürütme için ayırmalar. |
Evet (Yüksek): Ağır web sayfalarının, medya açısından zengin DOM'lerin yüklenmesi veya silinen WebView örneklerinde destroy() çağrısının yapılamaması bu etiketi doğrudan şişirir. |
[anon:scudo...] veya [anon:libc_malloc] |
Android yerel yığın ayırıcıları (Scudo / jemalloc) | NDK kitaplıkları, JNI köprüleri ve yerel grafik ardışık düzenleri tarafından kullanılan genel C/C++ yerel ayırmaları. | Evet (Orta ila Yüksek): Yerel JNI sarmalayıcıları veya üçüncü taraf C++ bağımlılıkları, gezinmeler arasında serbest bırakılmamış ayırmaları koruduğunda büyüme gerçekleşir. |
[anon:...] (örneğin, [anon:quickjs_heap...]) |
Özel komut dosyası oluşturma veya yerel çalışma zamanları | Yerleşik JavaScript motorları, özel WebAssembly çalışma zamanları veya özel yerel arabellek havuzları. | Evet (bağlama bağlı): Yerel görünümlerle birlikte komut dosyası motorlarını çalıştıran ve çalışma zamanı bağlamalarını temizleyemeyen hibrit uygulamalarda yaygındır. |
Uygulama içi bellek API'lerinin sınırlamaları
Uygulama içi bellek API'leri (ör. Debug.getMemoryInfo veya ActivityManager.getProcessMemoryInfo) yalnızca çağrı işlemini ölçer.
Çoklu işlem modunda bu API'ler, izole edilmiş oluşturucu işlemi tarafından tüketilen belleği yakalayamaz. Doğru bir toplam bellek değerlendirmesi için dumpsys meminfo, Perfetto veya Android Studio Profiler gibi sistem araçlarını kullanın.
Karma uygulamada yüksek bellek kullanımını önceliklendirme
Yinelenen WebView etkileşimleri (ör. web bağlantılarını açma veya web destekli feed'lerde gezinme) sırasında açıklanamayan bellek artışını teşhis ederken sızıntının Java katmanından mı yoksa yerel motordan mı kaynaklandığını belirlemek için aşağıdaki önceliklendirme iş akışını kullanın:
Sızıntı türünü (Java ve yerel) ayırın: Tekrarlanan kullanıcı geçişlerinden (ör. web makalelerini açıp kapatma veya feed'lerde gezinme) önce ve sonra
dumpsys meminfo -a <var>PACKAGE_NAME</var> | grep -A 10 "Objects"komutunu çalıştırın.- Gözlem:
ActivitiesveWebViewssayıları sabit kalırsa (örneğin, 1-2 etkin örnek) uygulama,Activitybağlamlarını veya JavaWebViewörneklerini sızdırmıyor demektir.
- Gözlem:
Etkileşimler arasında bellek farkını ölçme (Zaman serisi izleme): Geçiş başına ayırma oranını hesaplamak için birden fazla kullanıcı etkileşiminde
dumpsys meminfoanlık görüntü alın:- Gözlem: Java yığını sınırlı ve sağlıklı kalır (kullanım sırasında yükselir ve çöp toplama işleminden sonra düşer) ancak Özel Diğer ve Yerel Yığın, geçiş başına birkaç megabayt artar. Bu, sızıntının tamamen ART çalışma zamanının dışındaki yerel bellekte olduğunu kanıtlar.
Standart Java yığın dökümlerinde (
.hprof) sorun gösterilmez.
- Gözlem: Java yığını sınırlı ve sağlıklı kalır (kullanım sırasında yükselir ve çöp toplama işleminden sonra düşer) ancak Özel Diğer ve Yerel Yığın, geçiş başına birkaç megabayt artar. Bu, sızıntının tamamen ART çalışma zamanının dışındaki yerel bellekte olduğunu kanıtlar.
Standart Java yığın dökümlerinde (
Anonim bellek haritalarını inceleyin: ADB'yi kullanarak işlem bellek haritalarını inceleyin (Bellek haritalarını ve ayırmalarını inceleme bölümüne bakın):
adb shell "cat /proc/$(pidof <var>PACKAGE_NAME</var>)/maps" | grep "anon:"- Gözlem: Bellek artışı
[anon:partition_alloc]veya yerleşik komut dosyası motoru yığınlarında yoğunlaşır ve JNI global referanslarında yavaş bir artış görülür. Bu, Java görünümleri değiştirilirken temel yerel sayfa nesnelerinin veya JavaScript bağlamalarının serbest bırakılmadığını gösterir.
- Gözlem: Bellek artışı
Düzeltme:
- Geri dönüştürülen veya atılan her
WebViewnesnesinin etkin komut dosyalarını (stopLoading()) açıkça durdurduğundan, geçmişi temizlediğinden vedestroy()yöntemini çağırdığından emin olun. - Kapatılan görünümlerle ilişkili özel JavaScript köprü geri çağırmalarını veya JNI genel referanslarını kaldırın.
Private Otherve işlem RSS'sinin gezinme geçişlerinden sonra sabitlendiğini doğrulayın.
- Geri dönüştürülen veya atılan her
Ek kaynaklar
Bellek ve WebView performansında hata ayıklama ve profil oluşturma hakkında daha fazla bilgi edinmek için aşağıdaki kaynaklara göz atın: