WebView belleğini yönetme ve teşhis etme

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, WebView web 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: Activity ve 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).
  • Yerel bellekte kaplanan yer: Oluşturulan grafikler, DOM ağacı ve JavaScript çalışma zamanı belleği dahil olmak üzere WebView belleğ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 maxHeap ile sınırlanan ve OutOfMemoryError ile 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:

  1. WebView öğesini üst kapsayıcısından (ViewGroup) kaldırın.
  2. Etkin yüklemeyi durdurun ve gezinme geçmişini temizleyin.
  3. Şu numaraya telefon et: destroy().
  4. null referansı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 ve WebView ö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 meminfo içinde, yerel C/C++ ayırmaları ve özel bellek eşlemeleri (ör. Chromium PartitionAlloc veya 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 Activity sı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. WebView Yerel 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 WebViews veya Activities artıyorsa ve temel değere dönmüyorsa uygulamanız Java WebView örneğini veya ana makineyi Activity sızdırıyor demektir (örneğin, ViewGroup.removeView() eksikliği veya tutulan dinleyici referansları nedeniyle). Sızdırılan bir Activity, 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 ve OutOfMemoryError çökmelerine neden olur.

  • Yerel veya DOM sızıntısı: Toplam işlem RSS'si ve Private Other artmaya devam ederken WebViews ve Activities sabit 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:

  1. 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:...}
    
  2. Oluşturucu işleminin bellek dökümünü PID'sini kullanarak inceleyin:

    adb shell dumpsys meminfo <var>RENDERER_PID</var>
  3. 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:

  1. 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: Activities ve WebViews sayıları sabit kalırsa (örneğin, 1-2 etkin örnek), uygulama Activity bağlamlarını veya Java WebView örneklerini sızdırmıyor demektir.
  2. 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 meminfo anlı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.
  3. 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.
  4. 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 ve destroy() öğ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 Other ve RSS'nin gezinme geçişlerinden sonra dengelendiğini onaylayın.

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: