وقتی نیاز به بررسی میزان استفاده از حافظه دارید، با ابزارهای ارزیابی سطح بالا شروع کنید تا مشخص کنید کدام دسته از حافظهها از اهمیت بیشتری برخوردارند.
اطلاعات حافظه دامپسیس
رایجترین روش برای مشاهدهی نمای کلی از میزان استفاده از حافظهی یک برنامه، dumpsys meminfo است. برای مشاهدهی جزئیات دقیق شامل تمام نگاشتهای حافظه، اغلب استفاده از پرچم -a مفید است:
adb shell dumpsys meminfo -a <package_name_or_pid>
در اینجا نمونهای از خروجی یک برنامه تحت فشار حافظه را مشاهده میکنید (به SwapPss بالا توجه کنید):
** MEMINFO in pid 4562 [com.android.memorylab] **
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 394 268 64 17440 4264 29416 18128 8040
Dalvik Heap 165 8 0 672 8196 40949 32757 8192
Dalvik Other 47 28 0 604 1132
Stack 112 108 4 312 120
...
TOTAL 33197 580 788 21920 167776 70365 50885 16232
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 12 32608
Native Heap: 268 4264
Code: 724 125672
Stack: 108 120
Graphics: 0 0
Private Other: 256
System: 31829
Unknown: 5112
TOTAL PSS: 33197 TOTAL RSS: 167776 TOTAL SWAP PSS: 21920
Objects
Views: 15 ViewRootImpl: 1
AppContexts: 5 Activities: 1
...
تفسیر دادهها
- مجموع Pss : اندازه متناسب مجموعه. این مجموع
Private Dirtyاست-
Private Clean+ سهم عادلانهی فرآیند شما از حافظه که با سایر فرآیندها (مانند تصویر بوت Zygote) به اشتراک گذاشته شده است. این بهترین معیار برای «میزان حافظهای است که این برنامه مسئول آن است» است.
-
- Private Dirty : این رمی است که فقط فرآیند شما از آن استفاده میکند و تغییر یافته است (یا حافظه ناشناس است). این مهمترین عدد برای یافتن نشتیها است زیرا این حافظه را نمیتوان حذف کرد.
- Private Clean : رمی که فقط فرآیند شما از آن استفاده میکند، اما یک کپی اصلاح نشده از یک فایل روی حافظه است (مانند یک فایل DEX). در صورت کمبود حافظه، سیستم عامل میتواند آن را حذف کند.
- SwapPss : حافظهای که فشرده شده و به ZRAM منتقل شده است. این اتفاق میتواند به این دلیل رخ دهد که سیستم تحت فشار حافظه است یا به این دلیل که Android Runtime فرآیندهای پسزمینه را برای فشردهسازی (انتقال صفحات بلااستفاده یا غیرفعال آنها به ZRAM) شناسایی کرده است. در مثال
dumpsys، تقریباً تمامNative HeapوDalvik Heapبه ZRAM منتقل شدهاند.
- خلاصه برنامه : یک دستهبندی سطح بالاتر که ردیفهای جزئی را در قالب بخشهای قابل فهم ترکیب میکند.
- اشیاء : برای ردیابی
Activitiesنشتشده و نمونههایViewRootImplمفید است. اگر از برنامه خود خارج شوید و GC را اجباری کنید، تعدادActivitiesباید به ۰ برگردد.
تمرین عملی: ارزیابی سریع با meminfo
این تمرین نشان میدهد که چگونه انواع مختلف تخصیصها در meminfo نمایش داده میشوند.
برنامه را اجرا کنید :
adb shell am start -W -n com.android.memorylab/.MainActivityحافظه پایه را بررسی کنید :
adb shell dumpsys meminfo -s com.android.memorylab(علامت
-sخلاصهای کوتاه و مختصر از دستهبندیهای حافظه ارائه میدهد).تخصیص حافظه جاوا : در برنامه، ۳ بار روی دکمهی تخصیص حافظه جاوا (۱۰ مگابایت) ضربه بزنید.
تغییر را مشاهده کنید :
meminfo -sدوباره اجرا کنید. باید ببینید که مقدار Java Heap تقریباً 30،000 کیلوبایت (30 مگابایت) افزایش مییابد.تخصیص حافظه اصلی : یک بار روی دکمهی «اختصاص حافظه اصلی (1 گیگابایت غیرقابل فشردهسازی)» ضربه بزنید.
تغییر را مشاهده کنید :
meminfo -sدوباره اجرا کنید. مقدار Native Heap اکنون باید افزایش چشمگیری (حدود ۱,۰۰۰,۰۰۰ کیلوبایت) را نشان دهد.
نمای جهانی
برای مشاهده میزان استفاده از حافظه در کل سیستم، دستور زیر را اجرا کنید:
adb shell dumpsys meminfo
این خلاصهای از میزان استفاده از رم، شامل کل رم، رم آزاد و فهرستی از فرآیندها که بر اساس PSS مرتب شدهاند را ارائه میدهد.
معیارهای کلیدی سیستم
- ZRAM : فضای ذخیرهسازی فشرده در RAM.
- DMA-BUF : بافرهای گرافیکی و حافظه مشترک. نسخههای مدرن اندروید به طور گسترده از DMA-BUF برای رندر رابط کاربری استفاده میکنند. اگر مجموع
DMA-BUFبسیار بالا است، فرآیندهای سنگین گرافیکی را با استفاده از/sys/kernel/debug/dma_buf/bufinfoبررسی کنید. - حافظه رم از دست رفته : حافظهای که توسط هسته یا هیچ فرآیندی (مثلاً مناطق رزرو شده توسط سختافزار) در نظر گرفته نشده است.
نمایش نقشه
برای تجزیه و تحلیل دقیقتر نگاشتهای حافظه یک فرآیند، از showmap استفاده کنید. این ابزار از /proc/<pid>/smaps میخواند.
adb shell showmap <pid>
showmap تمام نگاشتهای حافظه در فرآیند را فهرست میکند. نگاشتهای کلیدی که باید به دنبال آنها باشید عبارتند از:
-
[anon:scudo:primary]یا[anon:libc_malloc]: هیپ بومی. نسخههای مدرن اندروید از تخصیصدهندهی Scudo استفاده میکنند. -
*.art: تصاویر پشته جاوا (مثلاً تصویر بوت). -
[anon:dalvik-main space]: هیپ اصلی جاوا. -
*.so: کتابخانههای اشتراکی. -
*.{oat,dex,odex,vdex}: کد DEX کامپایل شده.
میتوانید خروجی را مرتب کنید تا بزرگترین مصرفکنندههای حافظه خصوصی را پیدا کنید:
# Sort by private clean + private dirty memory
adb shell showmap <pid> | awk '{print $6+$7, $0}' | sort -rn | head -n 10
خروجی نمونه:
virtual shared shared private private Anon Shmem File Shared Private
size RSS PSS clean dirty clean dirty swap swapPSS HugePages PmdMapped PmdMapped Hugetlb Hugetlb Locked # object
-------- -------- -------- -------- -------- -------- -------- -------- -------- --------- --------- --------- -------- -------- -------- ---- ------------------------------
2776 18176 5784 2823 0 3008 308 2468 4700 4700 0 0 0 0 0 0 47 [anon:scudo:primary]
560 952 876 657 316 0 560 0 12 12 0 0 0 0 0 0 8 /data/app/.../base.apk
448 393216 448 448 0 0 0 448 0 0 0 0 0 0 0 0 2 [anon:dalvik-main space]
...
تمرین عملی: کاوش عمیق با showmap
یاد بگیرید که چگونه نگاشتهای خاص حافظه را شناسایی کنید و بین حافظه تمیز و کثیف تفاوت قائل شوید.
- پاکسازی حافظه با Mmap : یک بار روی دکمهی Mmap Clean Anon Memory (10MB) در برنامهی MemoryLab ضربه بزنید.
دریافت شناسه فرآیند : رابط کاربری برنامه، PID آن را نشان میدهد. همچنین میتوانید آن را با این دستور پیدا کنید:
adb shell pidof com.android.memorylabبا showmap بازرسی کنید :
# Replace <PID> with the actual process ID from the previous step adb shell showmap <PID> | grep "memorylab-clean"شناسایی نگاشت : شما باید یک نگاشت ناشناس با برچسب
[anon:memorylab-clean]با اندازه مجازی دقیقاً 10240 کیلوبایت ببینید. توجه داشته باشید که مقدار private dirty برای این نگاشت احتمالاً 0 است. این حافظه ناشناس "تمیز" است زیرا ماmmapبرای رزرو فضای مجازی استفاده کردیم اما هنوز در آن چیزی ننوشتهایم (بنابراین در واقع هیچ رم فیزیکی مصرف نشده است).مقایسهی هیپ بومی : این را با نگاشتهای
[anon:scudo:primary]که اگر بعد از مرحلهی ۵ تمرینmeminfoاجرا میشدند، درshowmapظاهر میشدند، مقایسه کنید. از آنجایی که آن تخصیص ۱ گیگابایتی بومی در واقع در هر صفحه مینویسد، آن صفحات "dirty" هستند و به طور عمده در ستون private dirty نمایش داده میشوند.نکته حرفهای (اشیاء بزرگ) : به تخصیصهای هیپ جاوا از تمرین
meminfoتوجه کنید. از آنجا که آنها آرایههای بایتی ۱۰ مگابایتی هستند، احتمالاً درshowmapبه صورت[anon:dalvik-free list large object space]ظاهر میشوند. زمان اجرای اندروید (ART) اشیاء بزرگتر از ۱۲ کیلوبایت را در یک فضای شیء بزرگ (LOS) جداگانه قرار میدهد تا از تکهتکه شدن در هیپ اصلی جلوگیری شود.
پروکاستاتس
در حالی که meminfo یک تصویر لحظهای از وضعیت و میزان استفاده از حافظه توسط تمام برنامههای در حال اجرا ارائه میدهد، procstats با نمونهبرداری دورهای از وضعیت و میزان استفاده از حافظه، دادههای تاریخی را ارائه میدهد. این ابزار اصلی مورد استفاده برای نظارت بر سلامت حافظه در درازمدت است و همان چیزی است که گزارش استفاده از حافظه را در تنظیمات اندروید هدایت میکند.
# View stats for the last 3 hours
adb shell dumpsys procstats --hours 3
درک ایالتها
خروجی procstats میتواند بسیار زیاد باشد. مهمترین بخش، معیارهای داخل پرانتز کنار هر وضعیت فرآیند است:
Top: 100% (54MB-64MB-67MB / 49MB-58MB-61MB / 110MB-120MB-130MB over 4)
قالب به این صورت است: (درصد زمان در حالت) (minPss-avgPss-maxPss / minUss-avgUss-maxUss / minRss-avgRss-maxRss روی نمونهها)
- Pss (اندازه مجموعه متناسب) : حافظه مشترک به طور متناسب بین فرآیندها تقسیم میشود.
- Uss (اندازه مجموعه منحصر به فرد) : حافظهای که فقط توسط این فرآیند استفاده میشود (معادل Private Clean + Private Dirty).
- Rss (اندازه مجموعه مقیم) : کل حافظه فیزیکی استفاده شده، شامل تمام صفحات اشتراکی (تمایل به شمارش بیش از حد دارد).
چرا این مهم است:
- برنامه پایدار : حداقل/میانگین/حداکثر به هم نزدیک هستند.
- نشت حافظه : مقدار max به طور قابل توجهی بیشتر از مقدار min است و با افزایش تعداد نمونهها، میانگین آن به مرور زمان افزایش مییابد.
- میزان مصرف نامنظم : مقدار max بسیار بیشتر از مقدار avg است که نشاندهنده عملیات سنگین گذرا (مانند پردازش تصویر) است که اگر در زمان نامناسبی اتفاق بیفتند، ممکن است باعث OOM شوند.
حالتهای فرآیند
procstats برنامهها را در چندین ایالت (که اغلب به صورت خلاصه بیان میشوند) ردیابی میکند:
- Persist : فرآیندهای حیاتی سیستم که همیشه در حال اجرا هستند.
- بالا : برنامه در حال حاضر در پیشزمینه است و برای کاربر قابل مشاهده است.
- ImpFg (مهم در پیشزمینه): فرآیندهای حیاتی پسزمینه (مانند IMEها) یا برنامههایی با سرویس پیشزمینه.
- ImpBg (زمینه مهم): فرآیندهای پسزمینهای که برای سیستم مهم تلقی میشوند.
- Fgs / BFgs : سرویسهای پیشزمینه یا سرویسهای پیشزمینه محدود.
- Service / ServRst : سرویسهای پسزمینه یا سرویسهایی که در حال حاضر در حال راهاندازی مجدد هستند.
- Cached / LastAct : برنامههایی که برای راهاندازی مجدد سریع در پسزمینه ذخیره میشوند. حذف این برنامهها برای سیستم بیخطر است.
وضعیت حافظه دستگاه و آمار زمان اجرا
procstats همچنین مدت زمانی را که کل دستگاه تحت فشار حافظه بوده است، ردیابی میکند. به دنبال بخش Run time Stats بگردید:
Run time Stats:
SOff/Norm: +1h10m15s
SOn/Norm: +15m20s
Low: +2m10s
Crit: +45s
- خاموش / روشن : صفحه نمایش خاموش در مقابل صفحه نمایش روشن.
- Norm / Mod / Low / Crit : سطوح فشار حافظه (عادی، متوسط، کم، بحرانی).
اگر دستگاهی زمان قابل توجهی را در حالتهای Low یا Crit بگذراند، کند میشود زیرا هسته به شدت حافظه را بازپس میگیرد و Low Memory Killer (LMK) شروع به خاتمه دادن به فرآیندهای پسزمینه میکند. سپس میتوانید آمار هر بسته را بررسی کنید تا ببینید کدام برنامهها به طور خاص در زمانی که دستگاه در این حالتهای استرسزا قرار دارد، از حافظه بالایی استفاده میکنند.