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önetilmemesi bellek sızıntılarına, bellek yetersizliği (OOM) kilitlenmelerine ve uygulama performansının düşmesine neden olabilir.
Bu belgede WebView çok işlemli bellek modeli açıklanmakta, sızıntıları önlemek için yaşam döngüsünün nasıl düzgün şekilde yönetileceği anlatılmakta ve bellek sorunlarını teşhis etmeye yönelik pratik iş akışları sağlanmaktadır.
WebView bellek mimarisini anlama
WebView belleği etkili bir şekilde yönetmek için Android'in web içeriği için kaynakları nasıl ayırdığını anlayın:
Çok işlemli yürütme: Android 8.0 (API düzeyi 26) ve sonraki sürümlerde,
WebViewweb içeriğini uygulamanızın temel işlevlerinden birden fazla işlemde ayırır (düşük RAM'li cihazlarda tek bir işleme geri dönebilir):- Ana makine (tarayıcı) süreci:
Activityve Java veya Kotlin kodunuzun çalıştığı ana uygulama süreci. - Yalıtılmış oluşturma 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ı) süreci:
Yerel bellekte kaplanan yer: Oluşturulan grafikler, DOM ağacı ve JavaScript çalışma zamanı belleği dahil olmak üzere
WebViewbelleğin çoğu 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ğinin kullandığı gerçek belleği yakalamaz.Yerel belleğin sistem üzerindeki etkisi: Uygulamanın
maxHeapile sınırlanan veOutOfMemoryErrorile hızlıca başarısız olan Java yığın ayırmalarının aksine, yerel bellek sessizce gigabaytlarca büyüyebilir. Yayınlanmamış yerel bellek, fiziksel RAM ve takas alanını (zRAM) doldurduğunda Android'in düşük bellek sonlandırıcı (LMK) özelliği, belleği geri kazanmak için arka plan işlemlerini sonlandırmaya başlar. Bu işlem, ön planda çalışan uygulama sonlandırılmadan önce cihazın genel ç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 uygun yaşam döngüsü yönetimi çok önemlidir. Sık yapılan bir hata, düzeninizden bir WebView öğesini kaldırmanın veya bir Activity öğesinin otomatik olarak tamamlanmasına 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()) bir yıkım sırasını açıkça düzenlemeniz, etkin sayfa yürütmesini 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 yok edildiğinde düzgün bir kapatma sağlamak 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 durdurun ve gezinme geçmişini temizleyin.
- Şu numaraya telefon et:
destroy(). nullreferansını temizleyin.
Aşağıdaki örnekte, WebView öğesinin nasıl düzgün ş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();
}
Yok etme sonrası hafızayı anlama
destroy() numaralı işlevi ç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) WebView öncesi temel değerine hemen düşmediğini görebilirsiniz.
Bu davranış normaldir. 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 süreçte yerleşik olarak kalır. destroy()'nın temel amacı, kullanıcılar web destekli ekranlar arasında gezinirken Activity birikimli 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): İşlem tarafından doğrudan ayrılan ve diskteki bir dosya tarafından desteklenmeyen bellek (ör. yerel yığın ve JavaScript çalışma zamanı ayırmaları). Bu, web içeriğinizin birincil bellek maliyetini gösterir (Android Studio Profiler'da Ayrılan olarak etiketlenir).
Özel Bellek Alanı (PMF): Anonim RSS ve takasın (zRAM) toplamı. PMF, uygulamanızın sistem üzerinde oluşturduğu gerçek ve kaldırılamayan bellek yükünü yansıtır.
Tarayıcı PMF'si ve Oluşturucu PMF'si: Uygulamanızın ana işlemi tarafından kullanılan bellek ile yalıtılmış oluşturucu işlemi tarafından kullanılan bellek karşılaştırması. Yoğun web içeriği, özellikle oluşturucu 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ı izlemek, bellek büyümesinin tutulan Java referanslarından mı yoksa yalnızca yerel tahsislerden mi kaynaklandığını belirler.Özel Diğer ve Yerel Yığın:
dumpsys meminfoiçinde, yerel C/C++ ayırmaları ve özel bellek eşlemeleri (ör. ChromiumPartitionAllocveya yerleştirilmiş JavaScript çalışma zamanı yığınları) Java yığını yerine Yerel Yığın ve Özel Diğer altında 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 kapladığı alanı incelemek üzere aşağıdaki araçları ve teknikleri kullanın:
Profillendirme ve teşhis araçları
Bellek ayırmalarını 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şlerinde Memory Profiler'ı kullanarak
Activitysızıntılarını tespit etmek için kullanın.Perfetto 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 yığın 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 büyümesinin, tutulan Java çerçevesi nesnelerinden (ör. kullanıcı arayüzü bileşenleri) veya yerel ayırmalardan kaynaklanıp kaynaklanmadığını belirlemek için dumpsys meminfo'ın 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 işleyicilerinin ve paket ayırmalarının sayısı gösterilir. WebView teşhisleri için öncelikle Activities ve WebViews'ye odaklanın.
Hedef kullanıcı etkileşimini (ör. web ekranını açma ve kapatma) tekrar tekrar gerçekleştirin ve sayıları karşılaştırın:
Örnek sızıntısı: Her gezinmede
WebViewsveyaActivitiesartıyorsa ve temel değere dönmüyorsa uygulamanız JavaWebViewörneğini veya ana makineyiActivitysızdırıyor demektir (örneğin,ViewGroup.removeView()eksikliği veya tutulan dinleyici referansları nedeniyle). Sızdırılan birActivity, görünüm ağacının tamamını ve çözümlenmiş resim kaynaklarını belleğe sabitlediğinden, tekrar eden ziyaretler Java yığınını hızla tüketir veOutOfMemoryErrorçökmelerine neden olur.Yerel veya DOM sızıntısı: Toplam işlem RSS'si ve Private Other artmaya devam ederken
WebViewsveActivitiessabit kalırsa sızıntı, yayınlanmamış yerel kaynaklardan, DOM öğelerinden veya JavaScript motoru bağlamalarından kaynaklanıyordur. Bu tahsisler yerel bellekte bulunduğundan ve ART çöp toplayıcısını atladığından standart Java bellek sızıntısı algılama araçları tarafından görünmez kalır ve işletim sistemi uygulamayı sonlandırana kadar birikmeye devam eder.
KSA'yı kullanarak yalıtılmış oluşturucu sürecinin profilini oluşturma
dumpsys meminfo komutunu yalnızca uygulamanızın paket adıyla çalıştırmak, yalnızca ana ana makine işleminin belleğini verir. Web sayfalarının oluşturulduğu yalıtılmış oluşturucu sürecini 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"Çıkışta, yalıtılmış işlem kaydı ve PID'si RENDERER_PID (örneğin,
22155) gösterilir:Isolated #5: ProcessRecord{... 22155:com.google.android.webview.debug:sandboxed_process0:...}Oluşturucu işleminin bellek dökümünü PID'sini kullanarak inceleyin:
adb shell dumpsys meminfo <var>RENDERER_PID</var>Tarayıcı tarafındaki ayak izini değerlendirmek için ana makine uygulaması sürecini inceleyin:
adb shell dumpsys meminfo <var>PACKAGE_NAME</var>
Bellek eşlemelerini 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:
| Anı 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 içinde DOM ağaçları, oluşturma arabellekleri, V8 JavaScript yığını ve WebAssembly yürütme için ayrılan alanlar. |
Evet (Yüksek): Ağır web sayfalarının, medya açısından zengin DOM'ların yüklenmesi veya atılan WebView örneklerinde doğrudan destroy() çağrısı yapılmaması bu etiketi ş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-Yüksek): Yerel JNI sarmalayıcıları veya üçüncü taraf C++ bağımlılıkları, gezinmeler arasında yayınlanmamış ayırmaları koruduğunda büyüme meydana gelir. |
[anon:...] (örneğin, [anon:quickjs_heap...]) |
Özel komut dosyası oluşturma veya yerel çalışma zamanları | Yerleştirilmiş JavaScript motorları, özel WebAssembly çalışma zamanları veya özel yerel arabellek havuzları. | Evet (Bağlama bağlı): Yerel görünümlerin yanı sıra komut dosyası oluşturma motorlarını yürüten ve çalışma zamanı bağlamalarını temizlemeyen karma 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ğırma sürecini ölçer.
Çoklu işlem modunda bu API'ler, yalıtılmış oluşturma işlemi tarafından kullanılan belleği yakalayamaz. Toplam bellek değerlendirmesinin doğru olması 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
Tekrarlanan WebView
etkileşimler (ör. web bağlantılarını açma veya web destekli özet akışlarında gezinme) sırasında açıklanamayan bellek büyümesini 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) belirleyin: Tekrarlanan kullanıcı geçişlerinden (ör. web makalelerini açma ve kapatma veya feed'lerde kaydırma) ö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), uygulamaActivitybağlamlarını veya JavaWebViewörneklerini sızdırmıyor demektir.
- Gözlem:
Etkileşimler arasındaki bellek farkını ölçme (Zaman serisi izleme): Geçiş başına ayırma oranını hesaplamak için birden fazla kullanıcı etkileşimi arasında
dumpsys meminfoanlık görüntüleri yakalayın:- Gözlem: Java yığını sınırlı ve sağlıklı kalıyor (kullanım sırasında yükseliyor ve çöp toplama işleminden sonra düşüyor) ancak Private Other ve Native Heap, her geçişte birkaç megabayt artıyor. Bu, sızıntının tamamen ART çalışma zamanı dışındaki yerel bellekte olduğunu kanıtlar.
Standart Java yığın dökümleri (
.hprof) herhangi bir sorun göstermez.
- Gözlem: Java yığını sınırlı ve sağlıklı kalıyor (kullanım sırasında yükseliyor ve çöp toplama işleminden sonra düşüyor) ancak Private Other ve Native Heap, her geçişte birkaç megabayt artıyor. Bu, sızıntının tamamen ART çalışma zamanı dışındaki yerel bellekte olduğunu kanıtlar.
Standart Java yığın dökümleri (
Anonim Bellek Haritalarını İnceleme: ADB 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ştirilmiş komut dosyası oluşturma motoru yığınlarında yoğunlaşmıştır. Buna, JNI genel referanslarında yavaş bir artış eşlik eder. Bu, Java görünümleri değiştirilirken temel alınan 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
WebViewöğesinin etkin komut dosyalarını (stopLoading()) açıkça durdurduğundan, geçmişi temizlediğinden vedestroy()öğesini çağırdığından emin olun. - Kapatılan görünümlerle ilişkili özel JavaScript köprüsü geri çağırmalarını veya JNI genel referanslarını kaldırın.
Private Otherve RSS'nin gezinme geçişlerinden sonra dengelendiğini onaylayı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 bakın: