بودجه‌های حافظه سیستم

در Android 17 و نسخه‌های بالاتر، پلاتفرم حافظه فرایند سیستم را بااستفاده از دو سازوکار مکمل Linux cgroup v2 که درکنار محدودکننده حافظه عمومی برنامه کار می‌کنند مدیریت می‌کند:

  • کف‌های محافظت از دیمون سیستم (SystemMemoryBudgetManager با memory.min): رزروهای خودتنظیم cgroup v2 memory.min (تنظیم‌شده روی ۹۰٪ از مجموعه کاری مقیم مستقر هر دیمون و محدودشده در سطح جهانی به ۱۰٪ از کل RAM فیزیکی) از دیمون‌های تعاملی حیاتی و فرایندهای پوسته (system_server،‏ surfaceflinger، گرافیک composer HAL، audioserver،‏ cameraserver،‏ netd،‏ SystemUI، و راه‌انداز Home) تحت فشار حافظه محافظت می‌کند و جایگزین سنجاق کردن فایل PinnerService mlock ایستا می‌شود.
  • سقف حافظه برنامه سیستم (<memory-budget> با memory.high): محدودیت‌های نرم cgroup v2 memory.high کالیبره‌شده با حجم کاری (معمولاً ۳۰ مگابایت تا ۱۹۰ مگابایت) که در فایل‌های مانیفست برای برنامه‌های سیستم پس‌زمینه و دائمی (Bluetooth، Nfc، CalendarProvider، Traceur، MediaProvider، :PhotoPicker، NetworkStack، AppSearch، و TeleService) اعلام شده است، پیش‌از وقوع فشار حافظه سراسری، حافظه نهان صفحه فایل سرد و پشته ناشناس پس‌از انفجار را به‌طور پیش‌گیرانه بازیابی می‌کند.

جدول زیر این دو سازوکار را مقایسه می‌کند:

ابعاد کف‌های محافظت از دیمن سیستم سقف حافظه برنامه سیستم
سرویس پلاتفرم SystemMemoryBudgetManager MemoryLimiter
کنترل Cgroup v2 memory.min کف محافظت سخت memory.high سقف بازپس‌گیری ملایم
فرایندهای هدف رابط کاربری پوسته و دیمون‌های تعاملی حساس به تأخیر (system_server،‏ surfaceflinger،‏ composer HAL،‏ audioserver،‏ cameraserver،‏ netd،‏ SystemUI، و برنامه Home) برنامه‌های سیستم دائمی و بدون رابط کاربری، ارائه‌دهندگان محتوا، و سرویس‌های پس‌زمینه‌ای (مانند Bluetooth، Nfc، MediaProvider، NetworkStack، AppSearch، و TeleService)
مدل اندازه‌گیری خودتنظیمی در زمان اجرا (۹۰٪ از مجموعه کاری ثابت، با سقف جهانی ۱۰٪ از کل RAM) بدون جدول‌های تنظیم برای هر دستگاه مشخصه‌های XML مانیفست <memory-budget> اعلانی که براساس سطح RAM، وضعیت پردازش، و تعداد پیکسل‌های نمایشگر کالیبره شده‌اند
مزیت اصلی از لرزش میانای کاربر تعاملی و توقف‌های بیدار شدن تحت فشار حافظه جلوگیری می‌کند و درعین‌حال فهرست‌های فایل PinnerService mlock ثابت را جایگزین می‌کند به‌طور مداوم حافظه نهان فایل یک‌باره سرد و پشته پس‌از انفجار را تخلیه می‌کند تا RAM فیزیکی بیشتری برای برنامه‌های پیش‌زمینه و ذخیره‌شده در حافظه نهان باقی بماند

برای کسب اطلاعات بیشتر درباره نحو <memory-budget>، مشخصه‌های XML، میاناهای برنامه‌سازی کاربردی زمان اجرا، و حسابداری حافظه سیستم‌عامل، به تنظیم بودجه حافظه برنامه و درک و عیب‌یابی حافظه Android مراجعه کنید.

محافظت از دیمون‌های تعاملی حیاتی با SystemMemoryBudgetManager

SystemMemoryBudgetManager فرایندهایی را که مستقیماً در مسیر تعاملی کاربر قرار دارند، درطول فشار حافظه در RAM فیزیکی نگه می‌دارد. ‫SystemMemoryBudgetManager به‌جای سنجاق کردن فهرست ثابتی از فایل‌های APK،‏ ODEX، و VDEX به حافظه با mlock (همان‌طور که PinnerService برای config_defaultPinnerServiceFiles و config_pinnerHomePinBytes انجام می‌دهد)، کف‌های cgroup v2 پویا memory.min را به فرایندهای زیر اختصاص می‌دهد:

  • برش سیستم اصلی (/sys/fs/cgroup/system): system_server، surfaceflinger، گرافیک composer HAL،‏ audioserver، cameraserver، و netd، به‌علاوه هر سیستم بومی مختص فرم فاکتور یا دیمون‌های فروشنده فهرست‌شده در config_systemMemoryBudgetAdditionalSystemProcesses (برای مثال، android.hardware.perceptionhost و auracamerahal در دستگاه‌های XR).
  • برش برنامه تعاملی (/sys/fs/cgroup/apps): فرایند اصلی بسته SystemUI پیکربندی‌شده (config_systemUi) و فرایند اصلی دارنده نقش «صفحه اصلی» کاربر فعلی.

محافظت از دارنده نقش Home درکنار config_systemUi پوشش را در همه ضریب‌های شکل تضمین می‌کند. در عوامل فرمی که config_systemUi یک بسته سازگاری سبک است (مانند Wear OS، که صفحه ساعت، اعلان‌ها، و کاشی‌ها در داخل برنامه Home اجرا می‌شوند)، پوسته سمت کاربر همچنان محافظت کامل از مجموعه کاری را دریافت می‌کند. اگر Home intent به بسته config_systemUi منتهی شود، فرایند یک‌بار محافظت می‌شود. فقط فرایند اصلی هر بسته (processName == packageName) کف محافظت دریافت می‌کند، بنابراین فرایندهای فرعی کمکی (مثل :screenshot) بودجه محافظت را مصرف نمی‌کنند.

خط‌مشی تنظیم خودکار و واگذاری cgroup سه سطحی

ازآنجایی‌که مجموعه کاری یک دیمون تعاملی در انواع مختلف دستگاه‌ها و ظرفیت‌های RAM بسیار متفاوت است (برای مثال، SystemUI حدود ۴۵ مگابایت در ساعت هوشمند، ۶۰ مگابایت در تلفن ۶ گیگابایتی، و ۱۸۰ مگابایت در تلفن ۱۶ گیگابایتی استفاده می‌کند)، جدول‌های جستجوی ایستا برای هر دستگاه شکننده هستند. درعوض، SystemMemoryBudgetManager کف‌ها را مستقیماً از ردپای مقیم هر فرایند بااستفاده از سه ثابت به‌دست می‌آورد:

پارامتر خط‌مشی مقدار پیش‌فرض رفتار
کف هر فرایند ‫۹۰٪ از memory.current تسویه‌شده منهای حافظه نهان فایل نگاشت‌نشده از پشته ناشناس، اسلب، و کد نگاشت‌شده داغ محافظت می‌کند و درعین‌حال ۱۰٪ سردترین دنباله را قابل‌بازپس‌گیری می‌گذارد تا صفحه‌های واقعاً سرد همچنان بتوانند تخلیه شوند. حافظه نهان فایل نگاشت‌نشده (max(0, file - shmem - file_mapped) از memory.stat) قبل‌از اعمال نسبت ۹۰٪ کسر می‌شود تا حافظه نهان اسکن بسته راه‌اندازی یا خواندن دارایی یک‌باره باعث افزایش کف نشود.
محدودیت کلی RAM ‫۱۰٪ از کل RAM‏ (config_systemMemoryBudgetLowKb) با حفاظ PinnerService config_pinnerMaxPinnedMemoryPercentage مطابقت دارد. اگر مجموع ۹۰٪ کف‌ها از ۱۰٪ MemTotal بیشتر شود، هر کف به‌طور متناسب با همان ضریب کاهش می‌یابد.
آهنگ بازآوری ‫۱، ۴، ۱۶، و ۶۴ ثانیه پس‌از شروع، سپس هر ۳۰ دقیقه یک سری هندسی ثابت بر روی مجموعه کاری پس‌از راه‌اندازی هر دیمون همگرا می‌شود، بدون اینکه تأخیرهای گرم کردن برای هر دستگاه به‌صورت سخت‌کدگذاری شده باشد، و یک بازآوری حالت پایدار ۳۰ دقیقه‌ای رشد و کاهش تدریجی مجموعه کاری را در طول عمر دستگاه ردیابی می‌کند.

در کنترل‌کننده حافظه Linux cgroup v2 (mm/memcontrol.c:effective_protection)، محافظت مؤثر memory.min cgroup برگ به memory.min پیکربندی‌شده در cgroup والد و اجداد آن محدود می‌شود. تنظیم memory.min فقط در دایرکتوری برگ pid_<pid> درحالی‌که uid_<uid>/memory.min، 0 است منجر به محافظت مؤثر صفر می‌شود. برعکس، تنظیم memory.min ثابت فقط در برش بالایی /sys/fs/cgroup/system، محافظت مطالبه‌نشده را به‌طور متناسب بین همه شناسه‌های کاربری و فرایندهای فرزند توزیع می‌کند (memory_recursiveprot)، که به فرایندهای تشخیصی یا ثانویه که uid_1000 را هم‌رسانی می‌کنند (android.uid.system) اجازه می‌دهد محافظت را از surfaceflinger و audioserver بگیرند.

برای ارائه جداسازی دقیق برای هر دیمون، SystemMemoryBudgetManager در هر بازآوری، در هر سه سطح سلسله‌مراتب cgroup v2 محافظت می‌نویسد، همان‌طور که در شکل ۱ نشان داده شده است:

  1. گره فرایند برگ (pid_<pid>/memory.min): روی کف مجموعه کاری ۹۰٪ (مقیاس‌بندی‌شده) فرایند تنظیم شده است.
  2. گره UID (uid_<uid>/memory.min): روی مجموع دقیق برگه‌های مدیریت‌شده آن pid_* تنظیم شده است، بنابراین فرایندهای مدیریت‌نشده‌ای که آن UID را هم‌رسانی می‌کنند (مانند سایر AID_SYSTEM دیمن‌ها یا sharedUserId="android.uid.system" بسته‌ها) memory.min = 0 دارند و نمی‌توانند ادعای محافظت کنند.
  3. برش سطح بالا (/sys/fs/cgroup/system/memory.min و /sys/fs/cgroup/apps/memory.min): روی مجموع دقیق uid_* فرزندانش تنظیم شده است. هرگونه مقدار memory.low قدیمی در گره‌های مدیریت‌شده به 0 پاک می‌شود، و هرگونه دایرکتوری uid_* که در بازآوری قبلی مدیریت شده بود (مثل راه‌انداز «صفحه اصلی» قبلی پس‌از اینکه کاربر راه‌انداز را عوض می‌کند) به 0 بازنشانی می‌شود.

سلسله‌مراتب محافظت memory.min نسخه ۲ گروه کنترل سه‌سطحی در /sys/fs/cgroup/system و /sys/fs/cgroup/apps، که نشان می‌دهد کف فرایند برگ در گره‌های UID و برش جمع شده است درحالی‌که فرایندهای هم‌نیا محافظت‌نشده در صفر باقی می‌مانند

شکل ۱. واگذاری کف محافظت سه‌سطحی در بخش‌های cgroup.

چرا memory.min به‌جای memory.low در هسته‌های MGLRU استفاده می‌شود

در Multi-Generational LRU (MGLRU، موتور بازیابی صفحه پیش‌فرض در هسته‌های Android نسخه ۶.۶ و جدیدتر)، memory.low و memory.min در mm/vmscan.c:shrink_one رفتار متفاوتی دارند:

  • memory.low (به‌تعویق انداختن تک‌گذری): cgroup که استفاده آن کمتر از memory.low است به انتهای memcg LRU (MEMCG_LRU_TAIL) چرخانده می‌شود و برای یک گذر بازپس‌گیری رد می‌شود. در گذر بعدی تحت فشار حافظه پایدار، هسته cgroup را به‌طور کامل بازیابی می‌کند و یک رویداد MEMCG_LOW را ثبت می‌کند. در آزمایش‌های فشار دستگاه فیزیکی با رشد حافظه ناشناس تا ۸۵٪ از حافظه دسترسی تصادفی فیزیکی، memory.low به SystemUI در ساعت هوشمند اجازه داد از حدود ۴۵ مگابایت به حدود ۵ تا ۱۰ مگابایت کاهش یابد و audioserver در دستگاه تاشو ۱۶ گیگابایتی از ۱۵ مگابایت به ۲ مگابایت درمقابل حدود ۱۴ مگابایت کاهش یابد.
  • memory.min (کف محافظت سخت): cgroup که استفاده آن کمتر از memory.min باشد، توسط بازیابی سراسری نادیده گرفته می‌شود. همان‌طور که در شکل ۲ نشان داده شده است، تحت همان حجم کاری فشار ۸۵٪ RAM،‏ memory.min فقط سردترین ۱۰٪ دنباله هر دیمون را حذف می‌کند و SystemUI، surfaceflinger، و audioserver را در کف ۹۰٪ خود با صفر خطای مجدد ناشناس و بدون افزایش در پایان فرایند lmkd نگه می‌دارد.

memory.min یک رزرو فیزیکی سخت ارائه می‌دهد که با محافظت از سنجاق کردن PinnerService mlock که جایگزین آن می‌شود مطابقت دارد، با این مزیت که LRU هسته (به‌جای فهرست فایل‌های ایستا) انتخاب می‌کند کدام کد داغ، پشته، و صفحه‌های گرافیکی مقیم بمانند.

مقایسه حافظه مقیم دیمن با افزایش فشار حافظه سیستم از ۴۰٪ به ۹۵٪ حافظه دسترسی تصادفی فیزیکی، که نشان می‌دهد memory.min در کف ۹۰٪ خود باقی می‌ماند درحالی‌که memory.low پس‌از یک بار عبور تعویق MGLRU فرو می‌پاشد

شکل ۲. حفظ مجموعه کاری Daemon تحت فشار فزاینده حافظه.

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

جایگزین کردن PinnerService دائمی mlock config_defaultPinnerServiceFiles مستلزم هماهنگ کردن دو رفتار زمان راه‌اندازی است:

  • پیش‌گرم کردن بوت پس‌زمینه یک‌باره (PinnerService): رد شدن PinnerService mlock بدون گرم کردن حافظه پنهان صفحه، حدود ۹۵ مگابایت SystemUI خواندن دیسک APK،‏ ODEX، و VDEX را به مسیر بوت همگام منتقل می‌کند وقتی SystemUI شروع می‌شود (OnBootPhase_550)، ۳۰۰ تا ۴۵۰ میلی‌ثانیه به تکمیل بوت اضافه می‌کند. فراخوانی madvise(MADV_WILLNEED) کافی نیست زیرا force_page_cache_ra در هسته یک فراخوانی MADV_WILLNEED را به یک پنجره پیش‌خوان (معمولاً ۱۲۸ کیلوبایت تا ۲ مگابایت) محدود می‌کند. درعوض، وقتی SystemMemoryBudgetManager.shouldSkipDefaultSystemPinning فعال است، PinnerService هر فایل را در config_defaultPinnerServiceFiles روی یک رشته پس‌زمینه درطول onStart نگاشت می‌کند، فایل را به‌طور هم‌زمان با Os.mlock در حافظه پنهان صفحه پر می‌کند، و بلافاصله Os.munlock، munmap، و close را فراخوانی می‌کند. این کار زمان‌بندی راه‌اندازی سریع را حفظ می‌کند و درعین‌حال بایت‌های قفل‌شده دائمی را به صفر می‌رساند تا صفحات راه‌اندازی سرد بتوانند بعداً حذف شوند.
  • مستثنی کردن و بازپس‌گیری system_server حافظه نهان اسکن بسته راه‌اندازی: درطول راه‌اندازی، PackageManagerService فایل‌های APK نصب‌شده را اسکن می‌کند و ۱٫۵ تا ۲٫۵ گیگابایت حافظه نهان صفحه فایل نگاشت‌نشده سرد را به system_server cgroup اختصاص می‌دهد. اگر در محاسبه memory.min از system_server گنجانده شود، آن حافظه نهان راه‌اندازی سرد سقف ۱۰٪ حافظه دسترسی تصادفی سراسری را اشباع می‌کند و به‌طور متناسب کف‌های محافظتی surfaceflinger،‏ SystemUI، و audioserver را کاهش می‌دهد. برای جلوگیری از کمبود سقف، SystemMemoryBudgetManager memory.stat را برای system_server می‌خواند، حافظه نهان فایل نگاشت‌نشده را به‌صورت max(0, file - shmem - file_mapped) محاسبه می‌کند، و آن حافظه نهان نگاشت‌نشده را از memory.current قبل‌از محاسبه کف ۹۰٪ کم می‌کند، همان‌طور که در شکل ۳ نشان داده شده است. علاوه‌براین، ۱۰ ثانیه پس‌از PHASE_BOOT_COMPLETED، مدیر یک‌بار <unmapped_file> swappiness=0 را در گره memory.reclaim از system_server در هسته‌های Linux 6.2 و جدیدتر می‌نویسد تا حافظه نهان اسکن بسته سرد را بدون جابه‌جایی توده ناشناس یا بیرون کردن DEX نگاشت‌شده و صفحه‌های کد به‌طور پیش‌گیرانه بیرون کند. در هسته‌های قدیمی‌تر (مانند android14-6.1) که swappiness=0 را با EINVAL رد می‌کنند، مدیر از نوشتن memory.reclaim صرف‌نظر می‌کند و به نوشتن بازیابی نامشخصی که توده ناشناس را درطول راه‌اندازی مبادله می‌کند، برنمی‌گردد.

خط زمان حافظه system_server پس‌از راه‌اندازی که بازآوری‌های هندسی را در ثانیه‌های ۱، ۴، ۱۶، و ۶۴ نشان می‌دهد، حذف حافظه پنهان اسکن بسته راه‌اندازی نگاشت‌نشده از کف ۹۰٪ memory.min، و memory.reclaim یک‌باره با swappiness=0

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

الزامات هسته و هم‌پوشانی‌های پیکربندی دستگاه

SystemMemoryBudgetManager به mem_cgroup_calculate_protection در هسته Linux متکی است تا memory.min را در گروه‌های تودرتو منتشر کند. هسته‌های مشترک Android‏ android15-6.6 و android16-6.12 به‌طور پیش‌فرض از پشتیبانی کامل محافظت تودرتوی MGLRU برخوردارند. دستگاه‌هایی که android14-6.1 را با MGLRU فعال اجرا می‌کنند، به سه پورت برگشتی بالادست در mm/vmscan.c و mm/memcontrol.c نیاز دارند تا گروه‌های کنترل UID و برش میانی خالی، محافظت را به گروه‌های کنترل فرایند برگ انتشار دهند:

  • ‫30d77b7eef01‏ (mm/mglru: fix ineffective protection calculation)
  • ‫8aa420617918‏ (mm/mglru: respect min_ttl_ms with memcgs)
  • ‫adb8213014b2‏ (mm: memcg: fix stale protection of reclaim target memcg)

سازندگان تجهیزات اصلی می‌توانند SystemMemoryBudgetManager را بااستفاده از هم‌پوشانی‌های منبع چارچوب زیر سفارشی‌سازی یا غیرفعال کنند:

  • config_systemMemoryBudgetLowKb (عدد صحیح، کیلوبایت): 0 (پیش‌فرض) مجموع همه memory.min کف را به ۱۰٪ از کل RAM فیزیکی محدود می‌کند. مقدار مثبت سقف صریحی را برحسب کیلوبایت تنظیم می‌کند و مقدار منفی SystemMemoryBudgetManager را در دستگاه غیرفعال می‌کند (سنجاق کردن mlock ایستای PinnerService قدیمی را حفظ می‌کند).
  • config_systemMemoryBudgetHighKb (عدد صحیح، کیلوبایت): 0 (پیش‌فرض) /sys/fs/cgroup/system/memory.high را در max باقی می‌گذارد. مقدار مثبت سقف برش بالایی memory.high را تنظیم می‌کند (باید بزرگ‌تر یا مساوی با سقف کف باشد).
  • config_systemMemoryBudgetAdditionalSystemProcesses (string-array): Lists additional latency-critical native system or vendor daemon process names in /sys/fs/cgroup/system (matched against /proc/<pid>/comm or the basename of argv[0] in /proc/<pid>/cmdline for system and vendor UIDs under 10000) to protect with 90% memory.min floors alongside the default six system daemons. از این هم‌پوشانی به‌جای تنظیم memory.min در init.rc استفاده کنید زیرا SystemMemoryBudgetManager uid_<uid>/memory.min را به‌عنوان مجموع فرایندهای برگ مدیریت‌شده‌اش بازمحاسبه می‌کند و گره‌های uid_* مدیریت‌نشده را در هر بازآوری پاک می‌کند. برای مثال، یک دستگاه XR می‌تواند از ردیابی فضایی و دیمون‌های HAL دوربین خود با هم‌پوشانی زیر محافظت کند:

    <string-array translatable="false" name="config_systemMemoryBudgetAdditionalSystemProcesses">
        <item>android.hardware.perceptionhost</item>
        <item>auracamerahal</item>
    </string-array>
    
  • ‫config_systemUi (رشته): نام بسته برنامه SystemUI پلاتفرم، حل‌شده با MATCH_SYSTEM_ONLY.

برای بازرسی کردن SystemMemoryBudgetManager floors و گره‌های cgroup فعال در دستگاه، این دستور را اجرا کنید:

# Inspect policy state, resolved PIDs, and applied floors in dumpsys meminfo
adb shell dumpsys meminfo | sed -n '/System Memory Budgets/,$p'
# Inspect raw cgroup v2 memory.min nodes across the system and apps slices
adb shell head -n 1 \
  /sys/fs/cgroup/system/memory.min \
  /sys/fs/cgroup/system/uid_*/memory.min \
  /sys/fs/cgroup/system/uid_*/pid_*/memory.min \
  /sys/fs/cgroup/apps/memory.min \
  /sys/fs/cgroup/apps/uid_*/memory.min \
  /sys/fs/cgroup/apps/uid_*/pid_*/memory.min

محدود کردن برنامه‌های سیستم پس‌زمینه و دائمی با بودجه حافظه

درحالی‌که SystemMemoryBudgetManager از دیمون‌های تعاملی دربرابر بازپس‌گیری بااستفاده از کف‌های memory.min محافظت می‌کند، بسیاری از برنامه‌های سیستم دیگر و سرویس‌های پلاتفرم ممتاز برای مدت طولانی در پس‌زمینه به‌عنوان فرایندهای PERSISTENT، سرویس‌های پیش‌زمینه محدود، یا ارائه‌دهندگان محتوای مکرراً پرس‌وجوشده اجرا می‌شوند. بدون سقف حافظه صریح، این فرایندها تا زمانی که به محدودیت‌های چند گیگابایتی محدودکننده حافظه (مانند ۴ تا ۱۰ گیگابایت برای برنامه‌های قابل‌مشاهده و ۲ تا ۵ گیگابایت برای برنامه‌های پس‌زمینه) نرسند یا فشار حافظه سراسری باعث فعال شدن فعالیت kswapd در سراسر سیستم و حذف‌کننده حافظه کم (lmkd) نشود، محدود نمی‌شوند.

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

  • حافظه نهان صفحه فایل نامحدود: سرویس‌هایی که رسانه‌ها را نمایه می‌کنند، پایگاه‌های داده را جستجو می‌کنند، یا تقویم و جدول‌های ارائه‌دهنده را همگام‌سازی می‌کنند (مانند AppSearch، MediaProvider، و CalendarProvider) ازطریق فایل‌های بزرگ روی دیسک نگاشت یا جاری‌سازی می‌کنند. بدون سقف cgroup memory.high، صفحات تمیز با پشتیبانی فایل از خواندن‌های یک‌باره به‌طور نامحدود به cgroup فرایند شارژ می‌شوند و صدها مگابایت RAM فیزیکی را مصرف می‌کنند.
  • حفظ پشته ناشناس پس‌از انفجار: سرویس‌هایی که افزایش ناگهانی فعالیت گذرا را تجربه می‌کنند، مانند Bluetooth انفجارهای اکتشاف صوتی و BLE، NetworkStack اعتبارسنجی TLS درگاه مهمان، یا انتقال کمک‌کننده‌های واسط کاربر به پس‌زمینه، می‌توانند صفحات پشته ناشناس سرد را مدت‌ها پس‌از تکمیل انفجار حفظ کنند.

اعمال کردن <memory-budget> اعلان کالیبره‌شده (memory.high محدودیت نرم، معمولاً ۳۰ مگابایت تا ۱۹۰ مگابایت) در برنامه‌های سیستم دو مزیت اصلی دارد:

  • بازیابی صفحه محلی‌شده و پیش‌کنشی: وقتی فرایند سیستم از بودجه memory.high پیکربندی‌شده خود فراتر می‌رود، هسته Linux بازیابی صفحه را به‌طور خاص به آن cgroup هدایت می‌کند، ابتدا صفحات سرد با پشتیبانی فایل را تخلیه می‌کند و صفحات ناشناس غیرفعال را بدون انتظار برای فشار حافظه دستگاه جهانی به ZRAM فشرده می‌کند.
  • حافظه دسترسی تصادفی (RAM) بیشتر برای برنامه‌های پیش‌زمینه و مخزن برنامه ذخیره‌شده در حافظه نهان: محدود کردن حافظه‌های نهان فایل سرد در سرویس‌های سیستم همیشه روشن، ده‌ها تا صدها مگابایت حافظه دسترسی تصادفی (RAM) فیزیکی را در سراسر دستگاه بازیابی می‌کند، که این امر باعث کاهش لرزش میانای کاربر، کاهش فشار راه‌اندازی دوربین، و زنده نگه داشتن برنامه‌های کاربر بیشتر در حالت ذخیره‌شده در حافظه نهان می‌شود.

پیکربندی بودجه حافظه برنامه سیستم

برنامه‌های سیستم از همان نحو مانیفست <memory-budget>، آبشار حالت پردازش، مقیاس‌بندی تراکم نمایش، و قانون وضوح عبارت آخرین تطابق که برنامه‌های عمومی استفاده می‌کنند بهره می‌برند. برای فهرست کامل مشخصه‌ها، به مرجع مشخصه XML مراجعه کنید.

سازندگان تجهیزات اصلی می‌توانند عناصر <memory-budget> را مستقیماً در AndroidManifest.xml برنامه سیستم اعلام کنند یا آن‌ها را از فایل XML جداگانه‌ای بااستفاده از ویژگی additional_manifests در Android.bp ادغام کنند. طبق قرارداد، AOSP بودجه‌های برنامه سیستم مرجع را در فایل‌های مستقل *-budget.xml (در frameworks/base/core/memory_budgets/ برای بسته‌های پلاتفرم اصلی یا در کنار واحدهای مجزا مانند packages/modules/NetworkStack/memory_budgets/، packages/modules/AppSearch/apk/، و packages/services/Telephony/) ذخیره می‌کند و آن‌ها را با additional_manifests پیوست می‌کند:

android_app {
    name: "TeleService",
    manifest: "AndroidManifest.xml",
    additional_manifests: ["Telephony-budget.xml"],
    // ...
}

محدوده سطح بسته (UID) درمقابل سطح فرایند (PID)

اینکه بودجه پایه سطح بسته (<application>) یا بودجه چندفرایندی (<processes><process>) را اعلام کنید تعیین می‌کند که آیا هسته memory.high را در سطح cgroup شناسه کاربری (/sys/fs/cgroup/uid_<uid>/memory.high) یا در سطح cgroup شناسه فرایند (/sys/fs/cgroup/uid_<uid>/pid_<pid>/memory.high) اعمال می‌کند یا نه، همان‌طور که در شکل ۴ نشان داده شده است:

  • بودجه سطح بسته (<application>): زمانی استفاده می‌شود که APK یک فرایند واحد را تحت UID اختصاصی خود اجرا می‌کند (برای مثال، Bluetooth، Nfc، NetworkStack، CalendarProvider، Traceur، یا AppSearch).
  • بودجه سطح فرایند (<processes><process>): زمانی استفاده می‌شود که APK با دیگر بسته‌های سیستم هم‌رسانی می‌کندandroid:sharedUserId (مانند android.uid.phone با شناسه کاربر 1001 یا android.uid.system با شناسه کاربر 1000) تا دیگر فرایندهایی که آن شناسه کاربر را هم‌رسانی می‌کنند باهم محدود نشوند، یا زمانی که یک APK چندین فرایند را با نقش‌های حافظه متمایز اجرا می‌کند (برای مثال، دیمن پس‌زمینه MediaProvider و زیرفرایند میانای کاربری :PhotoPicker).

سلسله‌مراتب Cgroup v2 که اجرای memory.high در سطح UID را برای UIDهای اختصاصی با اجرای memory.high در سطح PID برای UIDهای سیستم مشترک و APKهای چندفرایندی مقایسه می‌کند

شکل ۴. سقف حافظه سطح UID درمقابل سطح PID.

مثال زیر MediaProvider-budget.xml را نشان می‌دهد که بودجه‌های رده‌بندی‌شده براساس RAM را به فرایند پیش‌فرض MediaProvider (<process>) و بودجه مقیاس‌بندی‌شده براساس نمایش را به فرایند واسط کاربر :PhotoPicker محدود می‌کند. هر مشخصه android:maxDeviceRamMb با دستگاه‌هایی مطابقت دارد که کل حافظه دسترسی تصادفی فیزیکی (MemTotal) آن‌ها برابر یا کمتر از آستانه مگابایت مشخص‌شده باشد (یا درصورت حذف، برای هر ظرفیت حافظه دسترسی تصادفی اعمال می‌شود). بندها از پیش‌فرض نامحدود تا کوچک‌ترین آستانه maxDeviceRamMb مرتب شده‌اند، بنابراین قانون حل‌وفصل بند آخرین تطابق، سخت‌ترین سطح قابل‌اجرا را انتخاب می‌کند:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.providers.media.module">
    <uses-sdk android:targetSdkVersion="10000" />
    <application>
        <processes>
            <process>
                <memory-budget
                    android:maxMb="85" />
                <memory-budget
                    android:maxMb="75"
                    android:maxDeviceRamMb="12288" />
                <memory-budget
                    android:maxMb="60"
                    android:maxDeviceRamMb="8192" />
            </process>
            <process android:process=":PhotoPicker">
                <memory-budget
                    android:maxMb="85"
                    android:additionalBytesPerDisplayPixel="25" />
            </process>
        </processes>
    </application>
</manifest>

نحوه کالیبره شدن بودجه‌های مرجع AOSP در دستگاه‌های Google Pixel

بودجه‌های حافظه برنامه سیستم مرجع در AOSP در سراسر دستگاه‌های Google Pixel با حافظه دسترسی تصادفی فیزیکی ۶ تا ۱۶ گیگابایت (شامل تلفن‌ها، دستگاه‌های تاشو، و رایانه‌های لوحی ۶، ۸، ۱۲، و ۱۶ گیگابایتی) و وضوح نمایشگر از حدود ۱٫۲ مگاپیکسل (۷۲۰×۱۶۰۰) تا حدود ۴٫۵ مگاپیکسل (۲۰۷۶×۲۱۵۲) کالیبره شده‌اند.

کالیبره کردن دو منبع داده مکمل را ترکیب کرد: دورسنجی فیلد cgroup v2 در سراسر ناوگان و جاروب‌های memory.high دستگاه فیزیکی کنترل‌شده در سراسر مسیرهای قطعی کاربر (CUJ).

تحلیل دورسنجی cgroup v2 در سراسر ناوگان

عکس‌های آنی دوره‌ای cgroup v2 در سراسر ناوگان دستگاه جمع‌آوری شد و براساس ردیف حافظه دسترسی تصادفی فیزیکی (کمتر از ۸ گیگابایت، ۱۲ گیگابایت، و بیشتر از ۱۶ گیگابایت) و دسته وضعیت فرایند (foreground،‏ perceptible،‏ background، و cached) تقسیم شد. شش اصل راهنمای تحلیل آماری بود:

  • Decompose memory.current into working set versus reclaimable file cache: Because memory.high governs resident cgroup charge (memory.current), each snapshot was decomposed using memory.stat into anonymous heap (anon), swapped anonymous pages (swap), and file-backed page cache (file), as shown in figure 5. مجموع تقاضای ناشناس غیرقابل‌فشرده‌سازی (anon + swap در صدک ۹۹) حداقل کف مجموعه کاری را برای بودجه تعیین می‌کند تا فرایند با خطر اتمام ZRAM مواجه نشود، درحالی‌که فاصله زیاد بین memory.current صدک ۹۹ و anon + swap صدک ۹۹ نشان‌دهنده حافظه نهان صفحه فایل است که memory.high می‌تواند آن را بازیابی کند، مشروط بر اینکه حافظه نهان نگاشت‌نشده و سرد باشد.
  • حساب حافظه‌ای که memory.high نمی‌تواند پس بگیرد: همه فایل‌ها و حافظه مشترک حافظه پنهان سرد نیستند. صفحه‌هایی که قفل شده‌اند (unevictable در memory.stat، مانند حافظه مشترک قفل‌شده با mlock یا RAM مهمان قفل‌شده یک ماشین مجازی که فرایندهای میزبان آن به cgroup برنامه شارژ می‌شود) یک کف سخت زیر بودجه تعیین می‌کنند، و صفحه‌های نگاشت‌شده (file_mapped) اغلب بخشی از مجموعه کاری فعال هستند. پیش‌از اینکه حافظه فایل را قابل‌بازپس‌گیری درنظر بگیرید، unevictable، shmem، و file_mapped را بررسی کنید. برای مثال، حدود ۱۰۰ مگابایت از حافظه AppSearch قفل شده است و قابل‌تخلیه نیست، بنابراین بودجه آن این کف را به‌علاوه مجموعه کاری میزبان فعال آن پوشش می‌دهد، با فضای اضافی برای رشد منطقه قفل‌شده.
  • فیلتر کردن رقیق‌سازی حالت بیکار: ارائه‌دهندگان محتوا (CalendarProvider) و ابزارهای توسعه‌دهنده درخواستی (Traceur) ۷۰٪ تا ۹۹٫۹٪ از عکس‌های آنی فیلد را درحالت بیکار با حدود ۱ تا ۳ مگابایت می‌گذرانند. محاسبه صدک‌های خام در همه عکس‌های آنی، تخمین‌های P95 و P99 را به‌شدت رقیق می‌کند. درعوض، صدک‌ها و فاصله‌های اطمینان در عکس‌های آنی فعال رقیق‌نشده محاسبه شدند (با فیلتر کردن عکس‌های آنی CACHED و عکس‌های آنی خط مبنای غیرفعال).
  • از فاصله‌های اطمینان خوشه‌بندی‌شده براساس دستگاه استفاده کنید: چون یک دستگاه درطول زمان عکس‌های آنی دوره‌ای زیادی ارائه می‌دهد، درنظر گرفتن عکس‌های آنی به‌عنوان نمونه‌های مستقل، عدم قطعیت دنباله را کمتر از واقعیت نشان می‌دهد. همه تخمین‌های مجموعه کاری P95 و P99 بااستفاده از فاصله‌های اطمینان ۹۵٪ خوشه‌بندی‌شده دستگاه محافظه‌کارانه (بااستفاده از تعداد دستگاه متمایز N_dev به‌عنوان اندازه نمونه مؤثر) ارزیابی شدند.
  • درنظر گرفتن فوران‌های گذرا با memory.peak: نمونه‌گیری دوره‌ای می‌تواند فوران‌های کوتاه‌مدت با تخصیص بالا را ازدست بدهد (مانند NetworkStack اعتبارسنجی TLS درگاه مهمان و کاوش‌های هم‌زمان ساناد، یا Bluetooth مقداردهی اولیه A2DP و BLE). برای سرویس‌های انفجاری، بودجه‌ها به‌اندازه‌ای تعیین شدند که صدک ۹۰ تا ۹۵ از بالاترین حد memory.peak هسته در طول عمر را در سطوح RAM پوشش دهند.
  • مطمئن شوید که سطح RAM نشان‌دهنده RAM است، نه نسل دستگاه: دستگاه‌های جدیدتر معمولاً هم RAM بیشتر و هم ویژگی‌های بیشتری دارند، بنابراین مجموعه کاری که با سطح RAM رشد می‌کند ممکن است درواقع با ویژگی‌ها رشد کند. قبل‌از افزودن سطح maxDeviceRamMb، داده‌ها را براساس مدل دستگاه تقسیم کنید. برای مثال، com.android.phone به همان P99 anon + swap (حدود ۸۵ تا ۹۰ مگابایت) در تلفن‌های ۱۲ گیگابایتی و ۱۶ گیگابایتی از همان نسل می‌رسد، درحالی‌که یک دستگاه تاشو قدیمی‌تر ۱۲ گیگابایتی از ۴۰ مگابایت استفاده می‌کند. بنابراین، بودجه آن از یک مقدار واحد بدون ردیف RAM استفاده می‌کند تا دستگاهی با RAM کمتر که ویژگی‌های تلفنی جدیدتر را ارائه می‌دهد، با کمبود بودجه مواجه نشود.

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

شکل ۵. مجموعه کاری درمقابل حافظه پنهان فایل قابل‌بازپس‌گیری.

CUJ دستگاه فیزیکی و جاروب‌های مقیاس‌بندی نمایشگر

برای اعتبارسنجی بودجه‌های مشتق‌شده از دورسنجی و کالیبره کردن بودجه‌های متناسب با نمایشگر (مانند :PhotoPicker) و بودجه‌های پس‌زمینه مختص وضعیت (مانند Traceur)، پاک‌سازی‌های زنده memory.high روی دستگاه‌های فیزیکی با حافظه دسترسی تصادفی (RAM) ۶ تا ۱۶ گیگابایت اجرا شد:

  • یافتن زانوی بدون خطای مجدد: هنگام اجرای CUJهای سرتاسری (مانند پرتاب سریع شبکه عکس، مرور آلبوم، تند کشیدن پیش‌نمایش عکس و ویدیو با وضوح بالا در حالت تمام‌صفحه، و ضبط و ذخیره ردگیری)، memory.high در حین اندازه‌گیری شمارنده‌های cgroup memory.stat (workingset_refault_anon، workingset_refault_file، pgscan، و pgsteal) و memory.events (high) کاهش یافت. همان‌طور که در شکل ۶ نشان داده شده است، بودجه بهینه درست بالای زانوی بدون خطای مجدد قرار دارد. زانوِ بدون خطای مجدد آستانه‌ای است که در آن هسته حافظه نهان فایل یک‌باره سرد را بازیابی می‌کند (pgsteal > 0) بدون اینکه باعث کوبش مجموعه کاری فعال شود (workingset_refault == 0).
  • اعتبارسنجی مقیاس‌بندی پیکسل نمایشگر در وضوح‌های مختلف: آزمایش :PhotoPicker در چهار وضوح نمایشگر فیزیکی و شبیه‌سازی‌شده از حدود ۱٫۲ مگاپیکسل (۷۲۰×۱۶۰۰) تا حدود ۴٫۵ مگاپیکسل (نمایشگر داخلی بازشده ۲۰۷۶×۲۱۵۲) تأیید کرد که مجموعه کاری نقشه بیتی Glide و ViewPager2 فعال آن به‌صورت خطی با تعداد پیکسل‌های نمایشگر مقیاس‌بندی می‌شود. خط پایه غیرنمایشگر نیز براساس دستگاه متفاوت است: در تعداد پیکسل تقریباً یکسان، زانوی بدون خطای مجدد حدود ۱۴۰ مگابایت در نمایشگر بیرونی تاشو ۱۶ گیگابایتی درمقابل ۱۱۰ تا ۱۲۵ مگابایت در تلفن ۶ گیگابایتی بود. خط پایه ۸۵ مگابایتی به‌علاوه ۲۵ بایت به‌ازای هر پیکسل نمایشگر (android:maxMb="85" و android:additionalBytesPerDisplayPixel="25") در همه نمایشگرهای آزمایش‌شده بالاتر از زانوی اندازه‌گیری‌شده باقی می‌ماند (حدود ۱۱۰ مگابایت در HD+‎، حدود ۱۴۵ مگابایت در FHD+‎، و حدود ۱۹۰ مگابایت در نمایشگرهای بازشده).
  • درستی‌سنجی جمع‌آوری زباله ART در حالت‌های پس‌زمینه: در cgroup v2، صفحه‌های تصویر چارچوب و boot.art هم‌رسانی‌شده با Zygote همچنان به cgroup مربوط به Zygote شارژ می‌شوند و درطول جمع‌آوری زباله فشرده‌سازی علامت‌گذاری هم‌زمان (CMC) مربوط به ART به‌عنوان فضای ایمن درنظر گرفته می‌شوند. آزمایش فشار تأیید کرد که وقتی برنامه واسط کاربر سیستم به background منتقل می‌شود، سخت‌گیرانه‌تر کردن memory.high (برای مثال، به ۳۰ مگابایت در Traceur) به‌طور تمیز پشته خصوصی و حافظه نهان صفحه را بدون ایجاد کوبش GC یا فعالیت مبادله محدود می‌کند.

نمودار بازیابی حافظه نهان سرد (pgsteal) و کوبش مجموعه کاری (workingset_refault) با کاهش تدریجی memory.high درطول CUJ، که زانوی بدون خطای ارجاع و بودجه هدف را برجسته می‌کند

شکل ۶. یافتن زانوی صفر-پیش‌فرض درطول CUJ.

خلاصه بودجه‌های برنامه سیستم مرجع AOSP

جدول زیر بودجه‌های مرجع کالیبره‌شده را در AOSP در سراسر سطوح RAM و حالت‌های پردازش خلاصه می‌کند:

بسته یا فرایند سیستم حوزه ‫≤۸ گیگابایت RAM ‫۱۲ گیگابایت حافظه دسترسی تصادفی ‫≥۱۶ گیگابایت حافظه دسترسی تصادفی درایور واسنجی اصلی
com.android.bluetooth UID (<application>) ‫۸۰ مگابایت ‫۹۵ مگابایت ‫۱۱۰ مگابایت Covers P90 lifetime memory.peak bursts (~75 to 110 MB) and >2x P99 active memory.current.
com.android.nfc UID (<application>) ‫۵۰ مگابایت ‫۶۰ مگابایت ‫۶۰ مگابایت «شبیه‌سازی کارت میزبان» فعال P99 (تا حدود ۵۰ مگابایت) و P90 تا P95 memory.peak را هنگام هرس کردن حافظه نهان فایل سرد پوشش می‌دهد.
com.android.providers.calendar UID (<application>) ۷۰ مگابایت پیش‌فرض (پیش‌زمینه)
۵۰ مگابایت perceptible و background
مجموعه کاری فعال در سطوح RAM ثابت است. بودجه پس‌زمینه فضای خالی ناشناسی را برای زنگ‌های هشدار یادآوری نگه می‌دارد و درعین‌حال حافظه پنهان صفحه SQLite را درطول همگام‌سازی‌ها کوتاه می‌کند.
com.android.traceur UID (<application>) ‫۸۵ مگابایت پیش‌فرض (پیش‌زمینه و قابل‌درک)
۳۰ مگابایت background
درطول ضبط و صادر کردن ردگیری فعال (TraceService)، فضای آزاد ۸۵ مگابایتی را به‌طور کامل حفظ می‌کند، درحالی‌که حالت غیرفعال و همه‌فرستی را به ۳۰ مگابایت محدود می‌کند.
‫com.android.providers.media.module (اصلی) شناسه محصول (<process>) ‫۶۰ مگابایت ‫۷۵ مگابایت ‫۸۵ مگابایت ~۱۰ تا ۳۰ مگابایت فضای خالی بیشتر از P99 anon + swap فراهم می‌کند و درعین‌حال حافظه پنهان فایل ریزعکس و SQLite سرد را بازیابی می‌کند.
com.android.providers.media.module:PhotoPicker شناسه محصول (<process>) ‫۸۵ مگابایت + ۲۵ بایت به‌ازای هر پیکسل
(تقریباً ۱۱۰ مگابایت در HD+‎، تقریباً ۱۴۵ مگابایت در FHD+‎، تقریباً ۱۹۰ مگابایت در تاشو ۴٫۵ مگاپیکسلی)
به‌طور متناسب با تعداد پیکسل‌های نمایشگر (additionalBytesPerDisplayPixel="25") مقیاس‌بندی می‌شود تا با زانوی مجموعه کاری نقشه بیتی Glide مطابقت داشته باشد.
com.android.networkstack UID (<application>) ‫۶۵ مگابایت ‫۷۰ مگابایت ‫۸۵ مگابایت حافظه عمدتاً ناشناس، با اندازه طول عمر P95 memory.peak برای پوشش دادن انفجارهای DHCP،‏ DNS، و TLS درگاه مهمان.
com.android.appsearch.apk UID (<application>) ‫۱۳۰ مگابایت حدود ۱۰۰ مگابایت حافظه قفل‌شده و غیرقابل‌تخلیه به‌علاوه حدود ۵ مگابایت مجموعه کاری میزبان فعال را پوشش می‌دهد، با فضای اضافی برای رشد منطقه قفل‌شده. خطای مجدد صفر در ≥۱۲۵ مگابایت اندازه‌گیری شد درحالی‌که حافظه نهان صفحه سرد (تا حدود ۳۳۰ مگابایت در P99) بازیابی شد.
‫com.android.phone‏ (TeleService) شناسه محصول (<process>) ‫۱۲۰ مگابایت محدوده هر PID در UID مشترک ۱۰۰۱. مجموعه کاری به‌جای RAM، نسل دستگاه را ردیابی می‌کند، بنابراین یک بودجه واحد حدود ۳۰ مگابایت بالاتر از بالاترین P99 anon + swap برای هر مدل قرار می‌گیرد.

چگونه سازندگان اصلی تجهیزات می‌توانند بودجه‌های برنامه سیستم را برای دستگاه‌های خودشان واسنجی کنند

تولیدکنندگان تجهیزات اصلی که برنامه‌های سیستم را سفارشی‌سازی می‌کنند (برای مثال، افزودن نمایه‌های بلوتوث فروشنده، افزونه‌های تلفنی سفارشی، یا نمایه‌سازهای رسانه تولیدکننده تجهیزات اصلی) یا سرویس‌های سیستم پیش‌نصب‌شده اضافی ارائه می‌دهند می‌توانند بودجه‌های موجود را مجدداً کالیبره کنند یا فایل‌های *-budget.xml جدیدی برای مجموعه سخت‌افزار خود ایجاد کنند.

جمع‌آوری نمایه‌های حافظه cgroup v2 فعال

برای هر فرایند سیستم هدف، گره‌های حافظه cgroup v2 آن را در /sys/fs/cgroup/uid_<uid>/pid_<pid>/ در سراسر حجم‌های کاری نماینده و سطوح RAM بخوانید:

  • ‫memory.current: حافظه مقیم فعلی که به cgroup شارژ شده است (به‌استثنای صفحه‌های مشترک Zygote)
  • ‫memory.peak: بالاترین حد memory.current در طول عمر از زمان شروع فرایند
  • ‫memory.swap.current: میزان استفاده فعلی از فضای مبادله فشرده‌شده در ZRAM
  • ‫memory.stat: تفکیک anon، file، pgscan، pgsteal، workingset_refault_anon، و workingset_refault_file
  • memory.events: تعداد تجمعی نقض‌های high (memory.high رویدادهای محدودسازی و بازپس‌گیری)

هنگام جمع‌آوری دورسنجی از دستگاه‌های آزمایشی یا میدانی، حالت‌های فرایند فعال جداگانه (TOP، FOREGROUND_SERVICE، اتصال‌های فعال، و همگام‌سازی‌های فعال) از حالت‌های غیرفعال CACHED یا غیرفعال PERSISTENT به‌گونه‌ای که عکس‌های آنی غیرفعال محاسبات مجموعه کاری P95 و P99 شما را به‌سمت پایین منحرف نکنند.

جدا کردن توده غیرقابل‌فشرده‌سازی از حافظه نهان فایل قابل‌بازپس‌گیری

مقایسه صدک نودونهم anon + swap با صدک نودونهم memory.current:

  • سرویس‌های تحت سلطه حافظه نهان فایل (file &gg; anon، مانند ارائه‌دهندگان پایگاه داده، نمایه‌سازهای جستجو، و دیمون‌های گزارش): android:maxMb را تنظیم کنید تا فضای راحتی (معمولاً ۱٫۵ تا ۴ برابر) نسبت‌به P99 anon + swap به‌علاوه یک پنجره کاری برای صفحات فایل داغ فراهم کند. این پیکربندی به هسته اجازه می‌دهد به‌طور مداوم صفحه‌های فایل سرد نگاشت‌نشده را بدون لمس کردن توده ناشناس تخلیه کند. اگر unevictable یا file_mapped بزرگ است (مانند حافظه قفل‌شده یا پایگاه‌های داده نگاشت‌شده در حافظه)، آن ردپا را نیز به‌عنوان مجموعه کاری حساب کنید.
  • سرویس‌های تحت‌تأثیر توده ناشناس یا سرویس‌های انفجاری (anon بیشتر memory.current را تشکیل می‌دهد، مثل پشته‌های شبکه‌سازی یا اتصال‌پذیری): اندازه android:maxMb برای پوشش دادن P90 تا P95 از memory.peak تا انفجارهای گذرا باعث راه‌اندازی تعویض همگام ZRAM در رشته‌های حساس به تأخیر نشود.
  • فرایندهای میانای کاربر با نمایشگر فشرده (مانند انتخاب‌گرها، گالری‌ها، یا کمک‌کننده‌های میانای کاربر سیستم): ردپای غیرمیانای کاربر خط پایه و ردپای نقشه بیتی افزایشی را به‌ازای هر مگاپیکسل در نمایشگرهای با کمترین و بیشترین وضوح اندازه‌گیری کنید و بودجه را بااستفاده از android:maxMb به‌علاوه android:additionalBytesPerDisplayPixel بیان کنید.

پاک‌سازی memory.high در دستگاه‌های فیزیکی

قبل‌از اختصاص دادن بودجه جدید به ساخت، از am memory-limiter manual استفاده کنید (یا مستقیماً در گره cgroup فرایند memory.high در ساخت userdebug بنویسید) تا محدودیت‌های نامزد را هنگام اجرای آزمایش‌های CUJ خودکارشده‌تان آزمایش کنید:

# Override memory.high for a specific PID to 60 MB during a CUJ test
adb shell am memory-limiter manual <PID> 60MB

memory.stat و memory.events را قبل و بعداز CUJ پایش کنید:

  • بودجه خیلی کم است: workingset_refault_anon یا workingset_refault_file درطول CUJ به‌شدت افزایش می‌یابد، memory.swap.current به‌طور مداوم درطول کار فعال پیش‌زمینه رشد می‌کند، یا تأخیر Binder یا قاب CUJ پس‌رفت می‌کند. بودجه را از زانوی پیش‌فرض بیشتر کنید.
  • بودجه خیلی زیاد است: memory.events:high حتی پس‌از حجم کاری سنگین نمایه‌سازی یا جاری‌سازی فایل، 0 باقی می‌ماند و file در memory.stat با صفحات سرد به‌طور نامحدود رشد می‌کند. بودجه را برای مجموعه کاری فعال محدود کنید.
  • بودجه به‌خوبی کالیبره شده است: memory.current حالت پایدار درطول عملکرد عادی زیر memory.high می‌ماند. درطول انفجارهای سنگین فایل یا حافظه پنهان، pgsteal صفحات فایل سرد را بازیابی می‌کند درحالی‌که workingset_refault_anon نزدیک به صفر باقی می‌ماند و تأخیر CUJ بدون تغییر می‌ماند.