WebView 是一項功能強大的元件,可讓您在 Android 應用程式中顯示網頁內容。不過,由於這基本上是功能齊全的瀏覽器引擎 (Chromium),因此記憶體用量相當大,且多重程序架構複雜。
技術背景:多程序架構
在搭載 Android 的新式裝置上,WebView 會使用多程序模型來提升安全性和穩定性。應用程式使用 WebView 時,記憶體會分配給不同程序:
- 瀏覽器程序 (應用程式程序):這是應用程式的主要程序。其中包含 Java
WebView物件,以及 Chromium 引擎的「瀏覽器」部分。這個程序會管理 UI、網路要求和 GPU 算繪 (直接與 Android HWUI 算繪管道整合)。與 Chrome 不同,WebView 沒有獨立的 GPU 程序。 - 轉譯器程序:這個程序負責剖析 HTML、執行 JavaScript 和版面配置。為確保安全,這項功能與系統的其餘部分隔離。目前,應用程式的所有 WebView 都只會取得一個轉譯器程序 (少數特殊情況除外),這與 Chrome 不同,Chrome 通常會為不同網站使用不同的轉譯器程序。

對記憶體的重要性
使用 dumpsys meminfo <your_package> 時,您只會看到瀏覽器程序 (您的應用程式程序) 所用的記憶體。Renderer 程序使用的記憶體會分開計算。
在瀏覽器程序中,WebView 記憶體的分配方式如下:
- Java 堆積:包含
WebViewJava 包裝函式和相關物件。 - 原生堆積:包含 Chromium 瀏覽器引擎的內部資料結構、快取和狀態。請注意,由於使用 PartitionAlloc,部分 WebView 原生分配項目可能不會計入
dumpsys meminfo的「原生堆積」中,而是顯示在「其他」或「不明」下方。 - 共用記憶體:用於共用圖形緩衝區和其他資料。這類內容可能無法由
dumpsys meminfo清楚分類。
疑難排解工具
Chrome DevTools
如要分析 WebView 內部的記憶體 (轉譯器程序),最強大的工具就是 Chrome 開發人員工具。
在應用程式中啟用 WebView 偵錯功能:
// NOTE: In production, this should be gated behind a developer setting // or only enabled for debuggable builds to prevent reverse engineering. WebView.setWebContentsDebuggingEnabled(true);使用 USB 連接裝置。
在主體機器上開啟 Chrome,然後前往
chrome://inspect/#devices。找到應用程式,然後按一下「檢查」。
在開發人員工具視窗中,前往「記憶體」分頁,即可擷取堆積快照或記錄 JavaScript 堆積的配置時間軸。
dumpsys meminfo
使用 adb shell dumpsys meminfo --all <package> 查看記憶體用量明細。在輸出內容中尋找 WebView 類別和物件計數。
剖析轉譯器
由於 Renderer 是在獨立程序中執行,因此您無法只剖析應用程式,必須找出 Renderer 程序的 PID。
如果有多個 WebView 處於啟用狀態,請按照下列步驟找出正確的算繪器 PID:
使用
dumpsys activity:adb shell dumpsys activity processes <your_package_name>尋找「
mConnections」部分。畫面會顯示ConnectionRecord,指出應用程式已連結至SandboxedProcessService。該程序的 PID 就是您的算繪器。範例:mConnections: - ConnectionRecord{... com.android.memorylab/org.chromium.content.app.SandboxedProcessService0:0 ...}檢查程序名稱:Renderer 程序通常命名為
com.google.android.webview:sandboxed_processX或類似名稱。如果只有一個應用程式使用 WebView,則可能只會有一個。
取得 PID 後,您可以使用 heapprofd 進行剖析。
WebView 記憶體最佳做法
明確銷毀
應用程式應呼叫 WebView.destroy(),指出何時完成執行個體。
雖然 WebView 會盡量確保執行個體可進行垃圾收集,並自動釋出所有資源,但很難保證 100% 的情況都能做到。即使自動垃圾收集功能正常運作,也可能會大幅延遲,導致應用程式保留資源的時間遠超出預期。
如果應用程式在適當時間 (例如 Activity.onDestroy() 中) 呼叫 WebView.destroy(),保留對 WebView 物件本身的參照,就不會洩漏任何重要原生資源。刪除 WebView 物件後,不一定需要將 Activity 欄位中的參照設為空值,因為系統會在垃圾收集 Activity 時清除這些參照。
練習:動手操作 WebView 記憶體
練習 1:觀察多程序足跡
啟動 MemoryLab,並對應用程式的記憶體進行基準測量:
adb shell dumpsys meminfo com.android.memorylab範例基準 (rango):
TOTAL PSS: 18915 KB輕觸「啟動 WebView (一般)」。
在 WebView 中,多次輕觸「Allocate JS Memory (1000 DIVs)」。
再次檢查應用程式的記憶體:
adb shell dumpsys meminfo com.android.memorylab觀察應用程式程序中的記憶體是否大幅增加 (與基準相比)!這是因為 DOM 元素位於「Renderer Process」。
找出渲染程序:
adb shell ps -A | grep webview | grep sandboxed輸出內容範例:
u0_i9002 14227 1087 1632732 135880 do_epoll_wait 0 S com.google.android.webview:sandboxed_process0檢查渲染器程序的記憶體 (使用 PID):
adb shell dumpsys meminfo 14227觀察顯示程序的高 TOTAL PSS。在我們的範例執行中,幾次配置後,記憶體用量就跳到 ~55MB。請注意,JavaScript 分配 (由 V8 引擎處理) 通常會計入
dumpsys meminfo的「Private Other」或「Unknown」 (mmap) 區段,而不是 Dalvik 堆積。
練習 2:Java 端 WebView 洩漏
常見的錯誤是在靜態欄位或會造成記憶體流失的長期存留物件中,保留 WebView 例項。因為 WebView 物件是「錨點」,會保留原生資源和整個渲染程序,因此洩漏這個物件的代價非常高昂。

- 在 MemoryLab 中,輕觸「Launch WebView (Java Leak)」。
- 頁面載入後,活動會自動關閉 (模擬重複導覽和洩漏累積)。
- 輕觸按鈕 4 次。
查看應用程式中的
WebView執行個體數量:adb shell dumpsys meminfo com.android.memorylab在底部找到「物件」部分。您會看到
WebViews的計數增加到 4。rango 上的輸出內容範例 (4 個洩漏的執行個體):
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: 4擷取記憶體快照資料,並使用 AHAT 找出記憶體流失問題。如果路徑中沒有
ahat,可以從 Android 樹狀結構建構:# 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.hprof在 AHAT 網頁介面 (
localhost:8888) 中,按一下頂端選單中的「allocations」(配置) 連結 (或「sites」(網站)),即可查看整體記憶體用量。
搜尋
android.webkit.WebView類別。按一下執行個體計數,即可查看所有即時執行個體。清單中應該會顯示多個執行個體。
按一下其中一個外洩的
WebView執行個體。向下捲動至「Sample Path from GC Root」(從 GC 根目錄取得的範例路徑) 部分。您會看到該工作由com.android.memorylab.WebViewActivity中的sLeakedWebViews清單保留。