WebView to zaawansowany komponent, który umożliwia wyświetlanie treści internetowych w aplikacji na Androida. Jest to jednak w zasadzie pełnoprawny silnik przeglądarki (Chromium), dlatego ma znaczące wykorzystanie pamięci i złożoną architekturę wieloprocesową.
Informacje techniczne: architektura wieloprocesowa
Na nowoczesnych urządzeniach z Androidem WebView używa modelu wieloprocesowego, aby zwiększyć bezpieczeństwo i stabilność. Gdy aplikacja używa WebView, pamięć jest rozdzielana między różne procesy:
- Proces przeglądarki (proces aplikacji): jest to główny
proces aplikacji. Zawiera obiekt Java
WebViewi część „przeglądarkową” silnika Chromium. Ten proces zarządza interfejsem, żądaniami sieciowymi i renderowaniem GPU (zintegrowanym bezpośrednio z potokiem renderowania Android HWUI). W przeciwieństwie do Chrome WebView nie ma oddzielnego procesu GPU. - Proces renderowania: ten proces jest odpowiedzialny za analizowanie kodu HTML, wykonywanie JavaScriptu i układ. Jest on odizolowany od reszty systemu ze względów bezpieczeństwa. Obecnie aplikacje otrzymują tylko 1 proces renderowania dla wszystkich komponentów WebView (z wyjątkiem kilku rzadkich przypadków specjalnych). W przeciwieństwie do Chrome, który często używa oddzielnych procesów renderowania dla różnych witryn.

Dlaczego jest to ważne w kontekście pamięci
Gdy używasz polecenia dumpsys meminfo <your_package>, widzisz tylko pamięć używaną przez
proces przeglądarki (proces aplikacji). Pamięć używana przez proces renderowania jest rozliczana oddzielnie.
W procesie przeglądarki pamięć WebView jest rozdzielana w następujący sposób:
- Stos Java: zawiera otokę Java
WebViewi powiązane obiekty. - Stos natywny: zawiera wewnętrzne struktury danych
, pamięć podręczną i stan silnika przeglądarki Chromium. Pamiętaj, że ze względu na użycie PartitionAlloc niektóre natywne alokacje WebView mogą nie być uwzględniane w sekcji „Native Heap” w
dumpsys meminfoi mogą zamiast tego pojawiać się w sekcji „Other” lub „Unknown”. - Pamięć współdzielona: służy do udostępniania buforów graficznych i innych danych. Może nie być wyraźnie sklasyfikowana przez
dumpsys meminfo.
Narzędzia do rozwiązywania problemów
Chrome DevTools
Najpotężniejszym narzędziem do analizowania pamięci wewnątrz WebView (procesu renderowania) są Narzędzia deweloperskie w Chrome.
Włącz debugowanie WebView w aplikacji:
// NOTE: In production, this should be gated behind a developer setting // or only enabled for debuggable builds to prevent reverse engineering. WebView.setWebContentsDebuggingEnabled(true);Podłącz urządzenie przez USB.
Otwórz Chrome na komputerze hosta i przejdź do
chrome://inspect/#devices.Znajdź swoją aplikację i kliknij inspect.
W oknie Narzędzi deweloperskich otwórz kartę Memory (Pamięć), aby zrobić zrzuty sterty lub zarejestrować osie czasu alokacji dla sterty JavaScriptu.
dumpsys meminfo
Aby zobaczyć podział pamięci, użyj polecenia adb shell dumpsys meminfo --all <package>.
W danych wyjściowych poszukaj kategorii WebView i liczby obiektów.
Profilowanie renderera
Ponieważ renderer działa w osobnym procesie, nie możesz profilować jego sterty natywnej, profilując tylko swoją aplikację. Musisz zidentyfikować PID procesu renderowania.
Aby zidentyfikować prawidłowy PID renderera, gdy aktywnych jest kilka komponentów WebView:
Użyj
dumpsys activity:adb shell dumpsys activity processes <your_package_name>Poszukaj sekcji
mConnections. ZobaczyszConnectionRecordłączący Twoją aplikację zSandboxedProcessService. PID tego procesu to Twój renderer. Przykład:mConnections: - ConnectionRecord{... com.android.memorylab/org.chromium.content.app.SandboxedProcessService0:0 ...}Sprawdź nazwy procesów: procesy renderowania mają zwykle nazwy
com.google.android.webview:sandboxed_processXlub podobne. Jeśli tylko 1 aplikacja używa WebView, prawdopodobnie będzie tylko 1 proces.
Gdy masz już PID, możesz profilować go za pomocą heapprofd.
Sprawdzone metody zarządzania pamięcią WebView
Jawne niszczenie
Aplikacje powinny wywoływać WebView.destroy(), aby wskazać, kiedy kończą korzystanie z instancji.
WebView próbuje zapewnić, że instancje mogą być zbierane przez moduł odśmiecania i automatycznie zwalniać wszystkie swoje zasoby, ale nie można tego zagwarantować w 100% przypadków. Nawet jeśli automatyczne odśmiecanie pamięci działa, może być znacznie opóźnione, co spowoduje, że aplikacja będzie przechowywać zasoby znacznie dłużej niż oczekiwano.
Jeśli aplikacja wywoła WebView.destroy() w odpowiednim momencie (np. w
Activity.onDestroy()), przechowywanie odniesienia do samego obiektu WebView nie spowoduje wycieku żadnych istotnych zasobów natywnych. Nie ma ścisłej potrzeby ustawiania wartości null dla odniesień do obiektu WebView w polach aktywności po jego zniszczeniu, ponieważ zostanie on wyczyszczony, gdy sama aktywność zostanie zebrana przez moduł odśmiecania.
Ćwiczenia: praktyczne korzystanie z pamięci WebView
Ćwiczenie 1. Obserwowanie zajętości pamięci przez procesy
Uruchom MemoryLab i wykonaj pomiar bazowy pamięci aplikacji:
adb shell dumpsys meminfo com.android.memorylabPrzykładowa linia bazowa (rango):
TOTAL PSS: 18915 KBKliknij Launch WebView (Normal) (Uruchom WebView (normalny)).
W WebView kilka razy kliknij Allocate JS Memory (1000 DIVs) (Przydziel pamięć JS (1000 DIV-ów)).
Ponownie sprawdź pamięć aplikacji:
adb shell dumpsys meminfo com.android.memorylabZauważ, że pamięć w procesie aplikacji nie zwiększa się znacząco w porównaniu z linią bazową. Dzieje się tak, ponieważ elementy DOM znajdują się w procesie renderowania.
Znajdź proces renderowania:
adb shell ps -A | grep webview | grep sandboxedPrzykładowe dane wyjściowe:
u0_i9002 14227 1087 1632732 135880 do_epoll_wait 0 S com.google.android.webview:sandboxed_process0Sprawdź pamięć procesu renderowania (za pomocą jego PID):
adb shell dumpsys meminfo 14227Zauważ wysoki TOTAL PSS procesu renderowania. W naszym przykładowym uruchomieniu po kilku alokacjach wzrosła ona do ~55 MB. Pamiętaj, że alokacje JavaScriptu (obsługiwane przez silnik V8) zwykle przyczyniają się do sekcji Private Other (Prywatne inne) lub Unknown (Nieznane) (mmap) w
dumpsys meminfo, a nie do sterty Dalvik.
Ćwiczenie 2. Wyciek WebView po stronie Javy
Częstym błędem jest przechowywanie instancji WebView w polu statycznym lub w obiekcie o długim czasie życia, który powoduje wyciek. Ponieważ obiekt WebView jest „kotwicą” o dużej wadze, która przechowuje zasoby natywne i potencjalnie całe procesy renderowania, jego wyciek jest bardzo kosztowny.

- W MemoryLab kliknij Launch WebView (Java Leak) (Uruchom WebView (wyciek Javy)).
- Aktywność zostanie automatycznie zamknięta po załadowaniu strony (symulując powtarzającą się nawigację i gromadzenie wycieków).
- Kliknij przycisk 4 razy.
Sprawdź liczbę instancji
WebVieww aplikacji:adb shell dumpsys meminfo com.android.memorylabU dołu poszukaj sekcji Objects (Obiekty). Zobaczysz, że liczba
WebViewswzrosła do 4.Przykładowe dane wyjściowe (4 wyciekające instancje) na rango:
Objects Views: 51 ViewRootImpl: 5 AppContexts: 14 Activities: 5 Assets: 38 AssetManagers: 0 Local Binders: 55 Proxy Binders: 77 Parcel memory: 41 Parcel count: 68 Death Recipients: 3 WebViews: 4Zrób zrzut sterty i użyj AHAT , aby znaleźć wyciek. Jeśli nie masz
ahatw ścieżce, możesz go skompilować z drzewa Androida:# Dump heap from device adb shell am dumpheap com.android.memorylab /data/local/tmp/heap.hprof adb pull /data/local/tmp/heap.hprof # Run ahat using the built JAR (found in out/host/linux-x86/framework/) java -jar out/host/linux-x86/framework/ahat.jar -p 8888 heap.hprofW interfejsie AHAT (
localhost:8888) kliknij link allocations (alokacje) (lub sites (witryny)) w górnym menu, aby zobaczyć ogólne wykorzystanie pamięci.
Wyszukaj klasę
android.webkit.WebView. Kliknij instance count (liczbę instancji), aby zobaczyć wszystkie aktywne instancje. Na liście powinno się wyświetlić kilka instancji.
Kliknij jedną z wyciekających instancji
WebView. Przewiń w dół do sekcji Sample Path from GC Root (Przykładowa ścieżka od głównego elementu odśmiecania). Zobaczysz, że jest ona przechowywana na liściesLeakedWebViewswcom.android.memorylab.WebViewActivity.
← Native | ↑ Up | App code →