عیب‌یابی در سطح سیستم

مشکلات حافظه اغلب شامل اجزای متعددی در کل سیستم می‌شود. درک چگونگی مدیریت حافظه توسط هسته و چگونگی رقابت فرآیندها برای منابع، کلید حل مشکلات پیچیده است.

پرفتو برای تحلیل سیستم

Perfetto ابزار اصلی برای تجزیه و تحلیل در سطح سیستم است. این ابزار به شما امکان می‌دهد ردیابی را ثبت کنید که شامل موارد زیر باشد:

  • شمارنده‌های حافظه فرآیند : rss.anon ، rss.file و swap برای هر فرآیند.
  • آمار هسته : اطلاعات از /proc/vmstat و /proc/meminfo .
  • PSI (اطلاعات توقف فشار) : معیارهای دقیقی در مورد میزان توقف فرآیندها به دلیل فشار بر حافظه.
  • رویدادهای LMK : چه زمانی و چرا برنامه‌ی Low Memory Killer تصمیم به خاتمه دادن به یک فرآیند گرفت.
  • زمان‌بندی : فعالیت بازیابی حافظه ( kswapd ) را با میزان استفاده از CPU مرتبط می‌کند.

شمارنده‌های حافظه در Perfeto

هنگام مشاهده‌ی یک ردپا، گروه مسیر یک فرآیند را گسترش دهید و به سمت پایین اسکرول کنید تا شمارنده‌های مختلف حافظه مجازی را ببینید.

شمارنده‌های حافظه‌ی Perfetto که برخی از شمارنده‌های حافظه‌ی مجازی را در com.android.MemoryLab نشان می‌دهند

پیکربندی ردیابی برای شمارنده‌های حافظه

برای ثبت این شمارنده‌های هر فرآیند در یک ردیابی، پیکربندی Perfetto شما ( pbtxt ) باید شامل منابع داده زیر باشد:

  1. linux.ftrace به همراه kmem/rss_stat : این منبع مبتنی بر رویداد، تغییرات لحظه‌ای در RSS (اندازه مجموعه مقیم) را همزمان با به‌روزرسانی هسته، ثبت می‌کند. این منبع، داده‌های با وضوح بالا را برای rss.anon ، rss.file و swap فراهم می‌کند.

    data_sources: {
        config {
            name: "linux.ftrace"
            ftrace_config {
                ftrace_events: "kmem/rss_stat"
                # ... other events
            }
        }
    }
    
  2. linux.process_stats : این منبع نمونه‌برداری‌شده، وضعیت اولیه حافظه را برای همه فرآیندها و به‌روزرسانی‌های دوره‌ای فراهم می‌کند. این برای دیدن مقادیر مطلق حافظه در شروع ردیابی ضروری است.

    data_sources: {
        config {
            name: "linux.process_stats"
            process_stats_config {
                scan_all_processes_on_start: true
                proc_stats_poll_ms: 1000 # Optional periodic polling
            }
        }
    }
    

اطلاعات مربوط به واماندگی فشار (PSI)

PSI به شما می‌گوید که سیستم (یا یک فرآیند خاص) چقدر زمان را صرف انتظار برای منابع حافظه کرده است.

  • some : حداقل یک فرآیند در انتظار حافظه متوقف شده است.
  • full : تمام فرآیندهای غیر بیکار به طور همزمان متوقف شدند. این نشان دهنده یک گلوگاه شدید است.

شما می‌توانید مقادیر PSI را از طریق ADB بررسی کنید:

adb shell cat /proc/pressure/memory

قاتل حافظه کم (LMK)

LMK مسئول از بین بردن فرآیندها برای آزاد کردن حافظه در مواقعی است که سیستم تحت فشار است. در نسخه‌های مدرن اندروید، دیمن lmkd فضای کاربری، وقایع را در logcat ثبت می‌کند، در حالی که رویدادهای oom_kill در سطح هسته در dmesg ثبت می‌شوند.

# Check userspace LMKD
adb logcat | grep -i "lmkd"

# Check kernel OOM killer
adb shell dmesg | grep -i "oom_kill"

در Perfetto، رویدادهای LMK به عنوان نشانگرهایی در مسیرهای سراسری سیستم ظاهر می‌شوند. هر رویداد شامل PID فرآیند کشته شده و دلیل آن (مثلاً "حافظه پنهان خیلی کم است") است.


بازپس‌گیری هسته: تعویض و حذف

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

حافظه ناشناس و ZRAM

حافظه ناشناس (هیپ جاوا، هیپ بومی، پشته‌ها) هیچ فایل متناظری در حافظه ندارد. اندروید از ZRAM ، یک فضای فشرده swap در RAM، برای مدیریت این امر استفاده می‌کند.

  1. فشرده‌سازی : هسته، صفحات ناشناس غیرفعال را شناسایی کرده و آنها را فشرده می‌کند.
  2. جابجایی : صفحات فشرده‌شده به ناحیه ZRAM منتقل می‌شوند.
  3. جابجایی (Swap-in) : وقتی یک فرآیند به یک صفحه ZRAM دسترسی پیدا می‌کند، هسته آن را از حالت فشرده خارج کرده و دوباره در RAM معمولی قرار می‌دهد.

در اندروید، شمارنده‌های استاندارد swap لینوکس مانند pswpin و pswpout به طور خاص فعالیت این ZRAM را ردیابی می‌کنند، زیرا ZRAM به عنوان دستگاه swap اصلی (و معمولاً تنها) پیکربندی شده است.

بررسی وضعیت ZRAM

  • مجموع کلی : از /proc/meminfo برای مشاهده میزان پیکربندی ZRAM و میزان استفاده فعلی آن استفاده کنید.

    adb shell cat /proc/meminfo | grep Swap
    # Example output:
    # SwapCached:          0 kB
    # SwapTotal:     2097148 kB
    # SwapFree:      1850244 kB
    

    SwapTotal حجم کل دستگاه ZRAM است. SwapTotal - SwapFree مقدار داده فشرده شده‌ای است که در حال حاضر در ZRAM ذخیره شده است.

  • نسبت فشرده‌سازی : برای مشاهده‌ی میزان اثربخشی فشرده‌سازی، اندازه‌ی اصلی داده‌ها را با اندازه‌ی فشرده‌شده‌ی آن‌ها در دستگاه ZRAM مقایسه کنید.

    # Original (uncompressed) size of stored data
    adb shell cat /sys/block/zram0/orig_data_size
    # 524288000 (500 MB)
    
    # Compressed size of stored data
    adb shell cat /sys/block/zram0/compr_data_size
    # 104857600 (100 MB)
    

    در این مثال فرضی، داده‌ها با نسبت ۵:۱ فشرده می‌شوند. نسبت فشرده‌سازی واقعی به آنتروپی داده‌های فشرده‌نشده بستگی دارد.

  • سربار رم فیزیکی : خود ZRAM از رم برای مدیریت بلوک‌های فشرده‌شده استفاده می‌کند.

    adb shell cat /sys/block/zram0/mem_used_total
    # 115343360 (110 MB)
    

    این مقدار واقعی رم فیزیکی است که در حال حاضر توسط دستگاه ZRAM اشغال شده است (داده‌های فشرده شده + فراداده).

تمرین: نسبت‌های فشرده‌سازی ZRAM

در این تمرین، مشاهده خواهید کرد که چگونه انواع مختلف داده‌ها بر کارایی ZRAM تأثیر می‌گذارند و به شما کمک می‌کنند تا در هنگام تجزیه و تحلیل حافظه برنامه واقعی، درک درستی از آنچه انتظار می‌رود، داشته باشید.

  1. آماده‌سازی : مطمئن شوید که MemoryLab در حال اجرا است. روی «آزاد کردن همه تخصیص‌ها» ضربه بزنید.
  2. خط پایه : به آمار فعلی ZRAM در mm_stat توجه کنید:

    adb shell cat /sys/block/zram0/mm_stat
    # Columns: orig_data_size, compr_data_size, mem_used_total, ...
    
  3. داده‌های تصادفی (نسبت تقریبی ۱x) : روی «اختصاص حافظه بومی (۱ گیگابایت غیرقابل فشرده‌سازی)» ضربه بزنید. منتظر swap-out باشید ( vmstat را بررسی کنید یا ۱۰ ثانیه صبر کنید).

    • مشاهده : خواهید دید orig_data_size و compr_data_size تقریباً به یک اندازه افزایش می‌یابند. داده‌های تصادفی آنتروپی بالایی دارند و نمی‌توان آنها را فشرده کرد. این بدترین حالت ممکن است.
  4. همه ۱ها (نسبت تقریباً ۴ برابر) : روی «آزاد کردن همه تخصیص‌ها» ضربه بزنید، سپس روی «تخصیص بومی (۱ گیگابایت‌ها)» ( 0xFF ) ضربه بزنید.

    • مشاهده : اندازه compr_data_size فقط حدود ۲۵۰ مگابایت افزایش می‌یابد. این یک مورد ایده‌آل برای فشرده‌سازی است. الگوریتم (معمولاً LZO یا LZ4) به راحتی الگوی تکرارشونده را شناسایی می‌کند.
  5. همه ۰ها (نسبت >۱۰۰x) : روی «آزاد کردن همه تخصیص‌ها» ضربه بزنید، سپس روی «تخصیص بومی (صفرهای ۱ گیگابایتی)» ( 0x00 ) ضربه بزنید.

    • مشاهده : نسبت فوق‌العاده بالایی از داده‌های فشرده‌نشده به داده‌های فشرده‌شده را مشاهده خواهید کرد. orig_data_size به میزان ۱ گیگابایت افزایش می‌یابد، اما compr_data_size و mem_used_total به سختی تغییر می‌کنند.
    • «راز» : این فشرده‌سازی نیست، بلکه یک میانبر هسته است. بک‌اند ZRAM ( zsmalloc ) صفحات پر از صفر را تشخیص می‌دهد و فشرده‌سازی را نادیده می‌گیرد. در عوض، صفحه را به عنوان یک کپی از صفحه صفر سراسری علامت‌گذاری می‌کند و تنها چند بایت از فراداده را مصرف می‌کند.

قوانین کلی ZRAM

هنگام تجزیه و تحلیل یک سیستم واقعی، می‌توانید نسبت‌های فشرده‌سازی معمول زیر را انتظار داشته باشید. این تخمین‌ها با فرض اندازه استاندارد صفحه ۴ کیلوبایت و در نظر گرفتن سربار مدیریت حافظه ZRAM انجام شده‌اند:

نوع داده نسبت معمول دلیل
صفحات صفر شده >100x از طریق میانبر «صفحۀ صفر» در هسته بهینه‌سازی شده است (از فشرده‌ساز صرف‌نظر می‌کند).
صفحات ثابت ~۴ برابر مقادیر تکراری (مثلاً 0xFF ) کاملاً فشرده می‌شوند، اما سربار هر صفحه و ترازبندی دال، نسبت مؤثر را محدود می‌کند.
متن / JSON / گزارش‌ها ~۲.۵ برابر تا ~۳.۵ برابر افزونگی بالا، اما آنتروپی بالاتر از یک بایت تکراری.
هیپ جاوا تقریباً ۲ برابر تا تقریباً ۳ برابر اشیاء کوچک زیاد با سرصفحه‌های مشابه و فیلدهای پراکنده.
کد ماشین (DEX/Native) ۱.۵ تا ۲ برابر دستورالعمل‌ها فشرده هستند اما الگوهای قابل تشخیصی دارند.
بیت‌مپ‌های رمزگشایی‌شده (رابط کاربری) تقریباً ۲ برابر تا تقریباً ۳ برابر اگر نواحی رنگی مسطح بزرگی (آیکون‌ها، پس‌زمینه‌ها) وجود داشته باشد، کارآمد است.
بیت‌مپ‌های رمزگشایی‌شده (عکس) ~۱.۱x تا ~۱.۲x آنتروپی بسیار بالا؛ مقادیر پیکسل‌ها بسیار متفاوت است.
داده‌های رمزگذاری‌شده/فشرده‌شده ~۱x ZRAM که از قبل آنتروپی بالایی دارد، نمی‌تواند بیشتر فشرده شود.

به همین دلیل است که ما از داده‌های تصادفی برای تمرین‌های فشار حافظه اصلی خود استفاده می‌کنیم: این بدترین حالت برای فشرده‌سازی است زیرا داده‌ها حداکثر آنتروپی را دارند، بنابراین جابجایی این صفحات به ZRAM حجم کل RAM را افزایش نمی‌دهد و بنابراین فشار حافظه فیزیکی را سریع‌تر از هر داده دیگری ایجاد می‌کند.

حذف حافظه پنهان صفحه

حافظه‌ی فایل-بک‌آپ (DEX، کتابخانه‌ها، دارایی‌ها) از طریق حافظه‌ی پنهان صفحه مدیریت می‌شود.

  • صفحات پاک : صفحاتی که با داده‌های موجود در حافظه مطابقت دارند. این صفحات می‌توانند فوراً توسط هسته حذف شوند.
  • صفحات کثیف (Dirty Pages) : صفحاتی که در RAM تغییر یافته‌اند اما هنوز در حافظه ذخیره نشده‌اند. این صفحات تا زمانی که نوشته نشوند، قابل حذف نیستند.

بررسی وضعیت حافظه پنهان صفحه

  • مجموع کلی : /proc/meminfo نشان می‌دهد که چه مقدار حافظه به حافظه پنهان صفحه اختصاص داده شده است.

    adb shell cat /proc/meminfo | grep -E "^(Cached|Active\(file\)|Inactive\(file\))"
    # Example output:
    # Cached:          1234567 kB
    # Active(file):     456789 kB
    # Inactive(file):   777778 kB
    

    هسته ترجیح می‌دهد ابتدا صفحات Inactive(file) را حذف کند. اگر Active(file) به طور قابل توجهی بزرگتر از Inactive(file) باشد، نشان می‌دهد که بیشتر حافظه پنهان صفحه به طور فعال استفاده می‌شود.

  • خطاهای تجمعی : تعداد دفعاتی که سیستم مجبور به بارگذاری صفحات از حافظه شده است را کنترل کنید.

    adb shell cat /proc/vmstat | grep -E "pgfault|pgmajfault"
    # Example output:
    # pgfault 12345678    # Total page faults (including minor/re-faults)
    # pgmajfault 1234     # Major faults (actually required disk I/O)
    

    «تقویت حافظه» زمانی است که زمان قابل توجهی صرف تعویض صفحات به داخل و خارج از رم می‌شود، به جای اینکه کد اجرا شود و کاربر به سمت هدف مورد نظر خود پیش برود. افزایش سریع شمارنده pgmajfault نشانه‌ی قوی از تقویت حافظه است.

فعالیت زمان اجرا با vmstat

برای مشاهده‌ی عملیات swap و ejiction به صورت بلادرنگ، از vmstat استفاده کنید.

adb shell vmstat 1
# Example output:
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  1  0 246904 123456  12345 800000    0    0   120     0 1234 5678  5  2 92  1  0

ستون‌های کلیدی برای فعالیت:

  • si / so : جابجایی و جابجایی از ZRAM. مقادیر غیر صفر در اینجا به این معنی است که هسته به طور فعال صفحات ناشناس را به/از حافظه فشرده منتقل می‌کند.
  • bi / bo : مسدود شدن و مسدود شدن (I/O). bi بالا در هنگام فشار بر حافظه، نشان‌دهنده‌ی خطاهای مکرر در حافظه‌ی نهان صفحه (thrashing) است.
  • wa (انتظار ورودی/خروجی) : درصد زمانی که پردازنده در حین انتظار برای ورودی/خروجی دیسک، بیکار بوده است. wa بالا در عمل، "صخره" عملکرد است.

kswapd و بازپس‌گیری مستقیم

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

  • استفاده زیاد از CPU kswapd : نشان می‌دهد که سیستم دائماً در یافتن صفحات خالی مشکل دارد.
  • بازپس‌گیری مستقیم : اگر kswapd نتواند به کار خود ادامه دهد، خود فرآیندها مجبور می‌شوند قبل از اینکه بتوانند تخصیص‌های خود را ادامه دهند، حافظه را به صورت همزمان بازپس بگیرند. این به عنوان رویدادهای "بازپس‌گیری مستقیم" در Perfetto نشان داده می‌شود.

ضربه و خطاهای مجدد

یک «خطای مجدد» زمانی رخ می‌دهد که هسته صفحه‌ای را که هنوز به طور فعال در حال استفاده است، حذف کند. اگر سیستم دائماً در حال حذف و بارگذاری مجدد همان صفحات باشد، در حال thrashing است.

Thrashing منجر به انتظار ورودی/خروجی بالا ( wa در ابزارهایی مانند top یا vmstat ) می‌شود. انتظار ورودی/خروجی نشان دهنده درصد زمانی است که CPU در حین انتظار برای تکمیل یک عملیات ورودی/خروجی دیسک (مانند بارگذاری مجدد یک صفحه DEX حذف شده) بیکار بوده است. wa بالا باعث می‌شود دستگاه حتی اگر کل استفاده از CPU کم به نظر برسد، پاسخگو نباشد.

تنظیم سیستم: swappiness

پارامتر swappiness تعیین می‌کند که آیا هسته باید حافظه ناشناس را مبادله کند یا حافظه فایل-بک‌آپ را حذف کند.

adb shell cat /proc/sys/vm/swappiness
  • محدوده : در هسته‌های لینوکس مدرن (۵.۸+)، محدوده بین ۰ تا ۲۰۰ است.
    • 0 : هسته فقط در مواقع اضطراری تعویض می‌شود.
    • ۱۰۰ : هسته با حافظه ناشناس و حافظه فایل-پشتیبانی‌شده به طور یکسان رفتار می‌کند.
    • ۲۰۰ : هسته به شدت ترجیح می‌دهد حافظه ناشناس را با ZRAM جابجا کند تا حداکثر حافظه پشتیبان فایل (حافظه پنهان صفحه) را در RAM نگه دارد.
  • مقادیر رایج اندروید : اکثر دستگاه‌های اندروید با قابلیت swappiness بالا، معمولاً بین ۱۰۰ تا ۱۶۰ (برخی حتی از ۲۰۰ استفاده می‌کنند) تنظیم شده‌اند. دلیل این امر این است که swap در ZRAM معمولاً سریع‌تر از خواندن صفحات برگشتی از حافظه UFS یا eMMC است و حفظ حافظه پنهان صفحه برای کد و داده‌های برنامه و سیستم برای عملکرد راه‌اندازی برنامه و پاسخگویی کلی سیستم بسیار مهم است. این را با تنظیمات معمول در دستگاه‌های دسکتاپ و سرور لینوکس که ۶۰ است مقایسه کنید، زیرا ذخیره‌سازی پایدار معمولاً در این دستگاه‌ها سریع‌تر است.

تمرین عملی: خطاهای صفحه، حافظه پنهان صفحه و تعویض

در این تمرین، شما از MemoryLab برای ایجاد فشار بر حافظه استفاده خواهید کرد و مشاهده خواهید کرد که چگونه هسته با استفاده از Perfetto به swap و حذف حافظه پاسخ می‌دهد.

۱. دستگاه را آماده کنید

مطمئن شوید که دستگاه یا شبیه‌ساز شما دسترسی روت ( adb root ) دارد.

برنامه را اجرا کنید و یک فایل آزمایشی بزرگ (مثلاً ۵۰۰ مگابایت) ایجاد کنید که بعداً آن را نگاشت خواهیم کرد:

  1. MemoryLab را باز کنید.
  2. روی ایجاد فایل آزمایشی (۵۰۰ مگابایت) ضربه بزنید.

صبر کنید تا لاگ‌ها تکمیل عملیات را نشان دهند.

۲. برای ردیابی آماده شوید

برای اطمینان از اینکه فایل از حافظه بارگذاری می‌شود، باید حافظه پنهان صفحه موجود را پاک کنیم.

# Stop the app to release its existing mappings
adb shell am force-stop com.android.memorylab

# Drop all clean caches
adb shell "echo 3 > /proc/sys/vm/drop_caches"

۳. یک رد عالی ثبت کنید

از پیکربندی‌ای استفاده کنید که شمارنده‌های vmstat و رویدادهای حافظه پنهان صفحه را ثبت کند.

شروع یک ردیابی کامل در پس‌زمینه که شمارنده‌های vmstat و رویدادهای حافظه پنهان صفحه را ثبت می‌کند:

adb shell perfetto -c - --txt \
  -o /data/misc/perfetto-traces/swap_exercise.perfetto-trace --background <<EOF
buffers: { size_kb: 131072 }
data_sources: {
    config {
        name: "linux.sys_stats"
        sys_stats_config { vmstat_period_ms: 250 }
    }
}
duration_ms: 60000
EOF

۴. فشار حافظه را تحریک کنید

  1. MemoryLab را اجرا کنید.
  2. روی Mmap thrash_test.bin (500 مگابایت فایل پشتیبان) ضربه بزنید. این فایل سفارشی ما را نگاشت می‌کند.
  3. چندین بار روی «اختصاص حافظه بومی (1 گیگابایت غیرقابل فشرده‌سازی)» ضربه بزنید. این کار را تا زمانی که دستگاه احساس کندی کندی کند، ادامه دهید. این کار هسته را مجبور می‌کند حافظه ناشناس را به ZRAM منتقل کند و در نهایت فایل نگاشت شده ما را از حافظه پنهان خارج کند.
  4. روی Thrash Pagecache (آزمون پیش‌فرض) ضربه بزنید. این گزینه فایل نگاشت‌شده را به‌طور مکرر می‌خواند و در صورت حذف، باعث ایجاد خطاهای مجدد می‌شود.

5. ردیابی را در Perfetto تجزیه و تحلیل کنید

ردیابی را در ui.perfetto.dev باز کنید.

مشاهده خطاها و تعویض آنها

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

نمایش کامل شمارنده‌های vmstat

مسیرهایی که می‌بینید، جنبه‌های مختلف وضعیت حافظه هسته را نشان می‌دهند:

  • شمارنده‌های حالت حافظه (مقادیر مطلق) : این مسیرها مقدار فعلی حافظه را در یک حالت خاص نشان می‌دهند. در تصویر، این مقادیر به صورت مقادیر مطلق (مثلاً بر حسب کیلوبایت یا تعداد صفحات) نمایش داده شده‌اند.

    • nr_free_pages : مقدار رم فیزیکی که کاملاً آزاد است.
    • nr_active_anon / nr_inactive_anon : حافظه ناشناس (مانند heapها و Stackها) که در حال حاضر استفاده می‌شود (فعال) یا مدتی است که به آن دسترسی نداشته است (غیرفعال). هسته ترجیح می‌دهد ابتدا صفحات غیرفعال را جابجا کند.
    • nr_active_file / nr_inactive_file : حافظه‌ی پشتیبان فایل (حافظه‌ی نهان صفحه) که فعال یا غیرفعال است. صفحات فایل غیرفعال اولین کاندیداها برای حذف شدن هستند.
  • شمارنده‌های فعالیت (نرخ‌ها) : برای شمارنده‌هایی که تعداد کل رویدادها را در طول زمان نشان می‌دهند، مانند خطاهای صفحه و عملیات تعویض، اغلب مشاهده نرخ رویدادها به جای مجموع تجمعی آنها مفیدتر است. در رابط کاربری Perfetto، می‌توانید مکان‌نما را روی یک مسیر شمارنده نگه دارید، روی نماد متریک کلیک کنید، سپس روی حالت کلیک کنید و بین مقدار ، دلتا یا نرخ یکی را انتخاب کنید. نمای نرخ، تشخیص انفجارهای فعالیت و مرتبط کردن آنها با سایر رویدادهای سیستم را بسیار آسان‌تر می‌کند.

    • pswpout (مخفف Swap Out) : نرخ فشرده‌سازی و انتقال صفحات ناشناس به ZRAM . مقادیر بالای آن نشان‌دهنده فشار شدید بر حافظه است.
    • pswpin (Swap In) : نرخی که فرآیندها با آن صفحات قبلی را که قبلاً به ZRAM منتقل شده‌اند، می‌خوانند.
    • pgfault (مجموع خطاهای صفحه) : نرخ تمام خطاهای صفحه، شامل آن‌هایی که بدون ورودی/خروجی دیسک مدیریت شده‌اند (خطاهای جزئی).
    • pgmajfault (عیوب اصلی صفحه) : نرخ خطاهایی که برای رفع آنها نیاز به ورودی/خروجی دیسک بوده است (مثلاً بارگذاری مجدد کد حذف شده از حافظه). این یک شاخص کلیدی از thrashing است.

در تصویر مشاهده خواهید کرد که وقتی nr_free_pages به طور قابل توجهی کاهش می‌یابد، همزمان با تلاش هسته برای آزاد کردن رم، شاهد افزایش ناگهانی در pswpout هستیم. بعداً، هنگامی که حافظه پنهان صفحه را "thrash" می‌کنیم، شاهد افزایش ناگهانی در pgmajfault و nr_active_file هستیم.

مشاهده‌ی حذف حافظه‌ی پنهان صفحه

برای یافتن رویدادهای حافظه پنهان صفحه در Perfetto: ۱. در نوار جستجو، mm_filemap_add_to_page_cache را تایپ کنید. ۲. رویدادها به صورت برش‌هایی در مسیرهای رویدادهای Ftrace (یک مسیر برای هر CPU) ظاهر می‌شوند. ۳. فرآیند MemoryLab را گسترش دهید. اگر ftrace به درستی پیکربندی شده باشد، می‌توانید فعالیت نقشه فایل را که با رشته‌های فرآیند مرتبط است، مشاهده کنید.

نمایش رویدادهای حافظه پنهان صفحه در ftrace به صورت عالی

  • mm_filemap_add_to_page_cache : نشان‌دهنده‌ی اضافه کردن یک صفحه به حافظه‌ی نهان صفحه است.
  • mm_filemap_delete_from_page_cache : نشان می‌دهد که یک صفحه از حافظه پنهان (cache) حذف شده است.
  • mm_filemap_fault : نشان‌دهنده‌ی خطای صفحه‌ای است که در یک فایل نگاشت‌شده در حافظه رخ داده است.

رفع خطاها در inodeها و سپس در فایل‌ها

روی یک رویداد add_to_page_cache جداگانه کلیک کنید. در پنل Details ، i_ino (شماره inode) را پیدا کنید. این شماره، فایل خاص را مشخص می‌کند.

یک رویداد add_to_page_cache که i_ino را نشان می‌دهد.

شما می‌توانید یک inode به مسیر فایل را به صورت دستی حل کنید:

# Replace <INODE_NUMBER> with the value from Perfetto
adb shell find /system /data /apex /data/user/0 -inum <INODE_NUMBER>

اسکریپت مفید: حل دسته‌ای inodeها

اگر رویدادهای زیادی دارید، می‌توانید از یک کوئری PerfettoSQL برای استخراج تمام inodeهای منحصر به فرد از ردیابی استفاده کنید و آنها را به طور خودکار با استفاده از یک اسکریپت کمکی حل کنید.

  1. استخراج ورودی‌ها : trace_processor برای دریافت مقادیر منحصر به فرد i_ino استفاده کنید:

    ./trace_processor -Q "SELECT DISTINCT int_value FROM args t JOIN raw r ON r.arg_set_id = t.arg_set_id WHERE r.name = 'mm_filemap_add_to_page_cache' AND t.key = 'i_ino'" \
      trace.perfetto-trace > inodes.txt
    
  2. حل کردن : inodeها را مستقیماً از ترمینال خود با یک حلقه سریع پوسته حل کنید:

    while read -r inode; do
      echo "Inode $inode -> $(adb shell find /system /data /apex /data/user/0 \
          -maxdepth 4 -inum "$inode" 2>/dev/null)"
    done < inodes.txt
    

نمونه خروجی از Pixel 10a:

Resolving 10 unique inodes for device localhost:27198...
Inode 14051 -> /data/user/0/com.android.memorylab/files/thrash_test.bin
Inode 1382 -> /system/framework/framework.jar
Inode 203 -> /system/bin/cmd
Inode 17998 -> /data/misc/logd/logcat

← سرویس‌های مقید | ↑ بالا | بازپس‌گیری →