وب ویو یک کامپوننت قدرتمند است که به شما امکان نمایش محتوای وب را در برنامه اندروید خود میدهد. با این حال، از آنجا که اساساً یک موتور مرورگر کامل (Chromium) است، حافظه قابل توجهی را اشغال میکند و معماری چندپردازشی پیچیدهای دارد.
پیشینه فنی: معماری چند فرآیندی
در دستگاههای مدرن مبتنی بر اندروید، WebView از یک مدل چند پردازشی برای بهبود امنیت و پایداری استفاده میکند. وقتی برنامه شما از WebView استفاده میکند، حافظه بین فرآیندهای مختلف توزیع میشود:
- فرآیند مرورگر (فرآیند برنامه) : این فرآیند اصلی برنامه شماست. این فرآیند شامل شیء Java
WebViewو بخش "مرورگر" موتور Chromium است. این فرآیند رابط کاربری، درخواستهای شبکه و رندر GPU (که مستقیماً با خط لوله رندر HWUI اندروید ادغام شده است) را مدیریت میکند. برخلاف کروم، WebView فرآیند GPU جداگانهای ندارد. - فرآیند رندرکننده : این فرآیند مسئول تجزیه HTML، اجرای جاوا اسکریپت و طرحبندی است. این فرآیند برای امنیت از بقیه سیستم جدا شده است. در حال حاضر، برنامهها فقط یک فرآیند رندرکننده برای همه WebViewها دریافت میکنند (به جز چند مورد خاص نادر)، برخلاف Chrome که اغلب از فرآیندهای رندرکننده جداگانه برای سایتهای مختلف استفاده میکند.

چرا این برای حافظه مهم است؟
وقتی از dumpsys meminfo <your_package> استفاده میکنید، فقط حافظهای را که توسط فرآیند مرورگر (فرآیند برنامه شما) استفاده میشود، میبینید. حافظهای که توسط فرآیند رندرکننده استفاده میشود، جداگانه محاسبه میشود.
در داخل فرآیند مرورگر، حافظه WebView به صورت زیر توزیع میشود:
- Java heap : شامل پوششدهندهی جاوای
WebViewو اشیاء مرتبط است. - Native heap : شامل ساختارهای داده داخلی، حافظههای پنهان و وضعیت موتور مرورگر Chromium است. توجه داشته باشید که به دلیل استفاده از PartitionAlloc، برخی از تخصیصهای بومی WebView ممکن است در
dumpsys meminfoتحت عنوان "Native Heap" شمارش نشوند و در عوض تحت عنوان "Other" یا "Unknown" ظاهر شوند. - حافظه مشترک : برای اشتراکگذاری بافرهای گرافیکی و سایر دادهها استفاده میشود. این ممکن است به وضوح توسط
dumpsys meminfoطبقهبندی نشده باشد.
ابزارهای عیبیابی
ابزارهای توسعه کروم
قدرتمندترین ابزار برای تجزیه و تحلیل حافظه درون WebView (فرآیند رندر) Chrome DevTools است.
اشکالزدایی 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://inspect/#devicesبروید.برنامه خود را پیدا کنید و روی inspect کلیک کنید.
در پنجرهی DevTools، به تب Memory بروید تا از حافظهی heap snapshot بگیرید یا جدول زمانی تخصیص حافظه برای حافظهی heap جاوا اسکریپت را ثبت کنید.
اطلاعات حافظه دامپسیس
برای مشاهدهی میزان حافظهی اشغال شده، adb shell dumpsys meminfo --all <package> استفاده کنید. در خروجی به دنبال دستهبندی WebView و تعداد اشیاء آن بگردید.
پروفایلبندی رندرکننده
از آنجایی که رندرکننده در یک فرآیند جداگانه اجرا میشود، نمیتوانید فقط با پروفایل کردن برنامه خود، هیپ بومی آن را پروفایل کنید. شما باید PID فرآیند رندرکننده را به طور خاص شناسایی کنید.
برای شناسایی PID رندرکننده صحیح هنگام فعال بودن چندین WebView:
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 ...}بررسی نام فرآیندها : فرآیندهای رندرکننده معمولاً با نام
com.google.android.webview:sandboxed_processXیا مشابه آن نامگذاری میشوند. اگر فقط یک برنامه از WebView استفاده میکند، احتمالاً فقط یکی وجود خواهد داشت.
وقتی PID را داشتید، میتوانید با استفاده از heapprofd آن را پروفایل کنید.
بهترین شیوهها برای حافظه WebView
تخریب آشکار
انتظار میرود برنامهها برای نشان دادن اینکه چه زمانی کارشان با یک نمونه تمام شده است، تابع WebView.destroy() را فراخوانی کنند.
اگرچه WebView تلاش میکند تا اطمینان حاصل کند که نمونهها میتوانند زبالهروب (garbage collector) شوند و تمام منابع خود را به طور خودکار آزاد کنند، اما تضمین این امر در ۱۰۰٪ موارد دشوار است. حتی زمانی که زبالهروب خودکار کار میکند، ممکن است به طور قابل توجهی به تأخیر بیفتد و باعث شود برنامه منابع را بسیار طولانیتر از حد انتظار نگه دارد.
اگر برنامهای در زمان مناسب (مثلاً در Activity.onDestroy() ) تابع WebView.destroy() را فراخوانی کند، نگهداشتن یک ارجاع به خود شیء WebView هیچ منبع بومی قابل توجهی را فاش نمیکند. نیازی به حذف ارجاعات به شیء WebView در فیلدهای Activity پس از از بین بردن آن نیست، زیرا وقتی خود Activity جمعآوری زباله میشود، این ارجاعات پاک میشوند.
تمرینها: کار عملی با حافظه WebView
تمرین ۱: مشاهدهی ردپای چندفرایندی
MemoryLab را اجرا کنید و یک اندازهگیری پایه از حافظه برنامه خود انجام دهید:
adb shell dumpsys meminfo com.android.memorylabنمونه خط مبنا (رنجو):
TOTAL PSS: 18915 KBروی راهاندازی وبویو (عادی) ضربه بزنید.
در WebView، چندین بار روی Allocate JS Memory (1000 DIV) ضربه بزنید.
دوباره حافظه برنامه را بررسی کنید:
adb shell dumpsys meminfo com.android.memorylabتوجه داشته باشید که حافظه در فرآیند برنامه شما در مقایسه با مقدار پایه، افزایش قابل توجهی ندارد! دلیل این امر این است که عناصر DOM در فرآیند رندر قرار دارند.
فرآیند رندر را پیدا کنید:
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به مجموع PSS بالای فرآیند رندرکننده توجه کنید. در اجرای نمونه ما، پس از چند تخصیص، به حدود ۵۵ مگابایت رسید. توجه داشته باشید که تخصیصهای جاوا اسکریپت (که توسط موتور V8 مدیریت میشوند) معمولاً به بخشهای Private Other یا Unknown (mmap) از
dumpsys meminfoکمک میکنند، نه به Dalvik Heap.
تمرین ۲: نشت WebView سمت جاوا
یک اشتباه رایج، نگهداشتن یک نمونه WebView در یک فیلد استاتیک یا در یک شیء با عمر طولانی است که نشت میکند. از آنجا که شیء WebView یک "لنگر" سنگین است که منابع بومی و احتمالاً کل فرآیندهای رندر را نگه میدارد، نشت آن بسیار پرهزینه است.

- در MemoryLab ، روی Launch WebView (Java Leak) ضربه بزنید.
- این فعالیت پس از بارگذاری صفحه به طور خودکار بسته میشود (شبیهسازی پیمایش مکرر و انباشت نشت).
- ۴ بار روی دکمه ضربه بزنید.
تعداد نمونههای
WebViewرا در برنامه خود بررسی کنید:adb shell dumpsys meminfo com.android.memorylabبه دنبال بخش اشیاء در پایین باشید. خواهید دید که تعداد
WebViewsبه ۴ افزایش یافته است.نمونه خروجی (۴ نمونه لو رفته) در رنگو:
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در مسیر خود ندارید، میتوانید آن را از درخت اندروید بسازید:# 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 برسید . خواهید دید که توسط لیستsLeakedWebViewsدرcom.android.memorylab.WebViewActivityنگهداری میشود.
← بومی | ↑ بالا | کد برنامه →