ابزارهای ارزیابی سریع

وقتی نیاز به بررسی میزان استفاده از حافظه دارید، با ابزارهای ارزیابی سطح بالا شروع کنید تا مشخص کنید کدام دسته از حافظه‌ها از اهمیت بیشتری برخوردارند.

اطلاعات حافظه دامپسیس

رایج‌ترین روش برای مشاهده‌ی نمای کلی از میزان استفاده از حافظه‌ی یک برنامه، 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 نمایش داده می‌شوند.

  1. برنامه را اجرا کنید :

    adb shell am start -W -n com.android.memorylab/.MainActivity
    
  2. حافظه پایه را بررسی کنید :

    adb shell dumpsys meminfo -s com.android.memorylab
    

    (علامت -s خلاصه‌ای کوتاه و مختصر از دسته‌بندی‌های حافظه ارائه می‌دهد).

  3. تخصیص حافظه جاوا : در برنامه، ۳ بار روی دکمه‌ی تخصیص حافظه جاوا (۱۰ مگابایت) ضربه بزنید.

  4. تغییر را مشاهده کنید : meminfo -s دوباره اجرا کنید. باید ببینید که مقدار Java Heap تقریباً 30،000 کیلوبایت (30 مگابایت) افزایش می‌یابد.

  5. تخصیص حافظه اصلی : یک بار روی دکمه‌ی «اختصاص حافظه اصلی (1 گیگابایت غیرقابل فشرده‌سازی)» ضربه بزنید.

  6. تغییر را مشاهده کنید : 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

یاد بگیرید که چگونه نگاشت‌های خاص حافظه را شناسایی کنید و بین حافظه تمیز و کثیف تفاوت قائل شوید.

  1. پاک‌سازی حافظه با Mmap : یک بار روی دکمه‌ی Mmap Clean Anon Memory (10MB) در برنامه‌ی MemoryLab ضربه بزنید.
  2. دریافت شناسه فرآیند : رابط کاربری برنامه، PID آن را نشان می‌دهد. همچنین می‌توانید آن را با این دستور پیدا کنید:

    adb shell pidof com.android.memorylab
    
  3. با showmap بازرسی کنید :

    # Replace <PID> with the actual process ID from the previous step
    adb shell showmap <PID> | grep "memorylab-clean"
    
  4. شناسایی نگاشت : شما باید یک نگاشت ناشناس با برچسب [anon:memorylab-clean] با اندازه مجازی دقیقاً 10240 کیلوبایت ببینید. توجه داشته باشید که مقدار private dirty برای این نگاشت احتمالاً 0 است. این حافظه ناشناس "تمیز" است زیرا ما mmap برای رزرو فضای مجازی استفاده کردیم اما هنوز در آن چیزی ننوشته‌ایم (بنابراین در واقع هیچ رم فیزیکی مصرف نشده است).

  5. مقایسه‌ی هیپ بومی : این را با نگاشت‌های [anon:scudo:primary] که اگر بعد از مرحله‌ی ۵ تمرین meminfo اجرا می‌شدند، در showmap ظاهر می‌شدند، مقایسه کنید. از آنجایی که آن تخصیص ۱ گیگابایتی بومی در واقع در هر صفحه می‌نویسد، آن صفحات "dirty" هستند و به طور عمده در ستون private dirty نمایش داده می‌شوند.

  6. نکته حرفه‌ای (اشیاء بزرگ) : به تخصیص‌های هیپ جاوا از تمرین 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) شروع به خاتمه دادن به فرآیندهای پس‌زمینه می‌کند. سپس می‌توانید آمار هر بسته را بررسی کنید تا ببینید کدام برنامه‌ها به طور خاص در زمانی که دستگاه در این حالت‌های استرس‌زا قرار دارد، از حافظه بالایی استفاده می‌کنند.


← مفاهیم | ↑ بالا | جاوا →