محدودکننده حافظه

‫Android 17 و نسخه‌های بالاتر شامل «محدودکننده حافظه» است، یک سرویس سیستم که بااستفاده از Linux cgroup v2، میزان استفاده از حافظه را در فرایندهای برنامه نظارت و محدود می‌کند. «محدودکننده حافظه» از مصرف نامتناسب حافظه سیستم توسط برنامه‌های مجزا جلوگیری می‌کند، که این امر فشار کلی روی حافظه را کاهش می‌دهد و از بستن تهاجمی فرایندهای مهم پیش‌زمینه و ذخیره‌شده در حافظه نهان به‌دلیل کمبود حافظه جلوگیری می‌کند.

سازوکار

«محدودکننده حافظه» با «سرویس مدیر فعالیت» (AMS) ادغام می‌شود تا رویدادهای چرخه حیات پردازش و تغییرات وضعیت را ردیابی کند. «محدودکننده حافظه» محدودیت‌های حافظه را بااستفاده از سلسله‌مراتب cgroup v2 هسته Linux اعمال می‌کند.

برای پشتیبانی از «محدودکننده حافظه»، هسته دستگاه باید cgroup v2 و کنترل‌کننده memory را فعال کند. این سرویس به‌طور خاص به مشخصه‌های زیر متکی است:

memory.high
محدودیت نرم. وقتی از این حد فراتر رود، فرایند محدود می‌شود و هسته تلاش می‌کند صفحه را به‌طور پیش‌گیرانه از آن فرایند پس بگیرد.
memory.swap.max
حداکثر مقدار فضای مبادله (مانند ZRAM) را که فرایند می‌تواند استفاده کند محدود می‌کند.

تأثیر بر برنامه‌ها

برنامه‌هایی که در محدوده حافظه خود کار می‌کنند تحت تأثیر «محدودکننده حافظه» قرار نمی‌گیرند.

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

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

پایش فرایند

«محدودکننده حافظه» به‌طور پیش‌فرض بر فرایندهای برنامه (UID >= 10000) نظارت می‌کند. فرایندهای اصلی سیستم برای حفظ پایداری ضروری سیستم مستثنی هستند.

«محدودکننده حافظه» محدودیت‌های حافظه را براساس وضعیت رؤیت‌پذیری فرایند اختصاص می‌دهد:

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

  • فرایندهای غیرقابل‌مشاهده: فرایندهایی که در پس‌زمینه کار انجام می‌دهند بدون اینکه واسط کاربری ارائه دهند (مانند سرویس‌های پس‌زمینه‌ای یا گیرنده‌های رویداد ثبت‌شده). چون مؤلفه‌های واسط کاربر را رندر نمی‌کنند، تحت محدودیت‌های سخت‌گیرانه‌تری عمل می‌کنند.

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

وضعیت پردازشمحدودیت حافظه
PERSISTENTنامحدود
PERSISTENT_UIنامحدود
TOPقابل‌مشاهده
BOUND_TOPقابل‌مشاهده
FOREGROUND_SERVICEغیرقابل‌رؤیت
BOUND_FOREGROUND_SERVICEغیرقابل‌رؤیت
IMPORTANT_FOREGROUNDقابل‌مشاهده
IMPORTANT_BACKGROUNDغیرقابل‌رؤیت
TRANSIENT_BACKGROUNDغیرقابل‌رؤیت
BACKUPغیرقابل‌رؤیت
SERVICEغیرقابل‌رؤیت
RECEIVERغیرقابل‌رؤیت
TOP_SLEEPINGقابل‌مشاهده
HEAVY_WEIGHTغیرقابل‌رؤیت
HOMEغیرقابل‌رؤیت
LAST_ACTIVITYغیرقابل‌رؤیت
CACHED_ACTIVITYدر حافظه پنهان
CACHED_ACTIVITY_CLIENTدر حافظه پنهان
CACHED_RECENTدر حافظه پنهان
CACHED_EMPTYدر حافظه پنهان

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

وقتی فرایندی از حد مجاز memory.high اختصاص‌داده‌شده به آن فراتر می‌رود، «محدودکننده حافظه» رویداد را شناسایی می‌کند و می‌تواند کنش‌های تشخیصی را راه‌اندازی کند، مثلاً ثبت کردن نمایه حافظه یا ثبت کردن ناهنجاری در statsd.

پیکربندی

در Android 17 و نسخه‌های بالاتر، «محدودکننده حافظه» بااستفاده از فایل پیکربندی استاندارد پلاتفرم در پارتیشن سیستم در /system/etc/memory-limiter-config.xml پیکربندی می‌شود.

فرمت XML

فایل پیکربندی از طرح تعریف‌شده در memory-limiter-config.xsd پیروی می‌کند. این فایل چندین مجموعه محدودیت را تعریف می‌کند که براساس حافظه دردسترس مرتب شده‌اند؛ سرویس بالاترین مجموعه محدودیت منطبق را براساس RAM دردسترس دستگاه انتخاب می‌کند. همه مقادیر حافظه برحسب مبی‌بایت (MiB) مشخص شده‌اند.

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <!-- RAM boundaries account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
      <maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
      <memVisible>2048</memVisible>
      <memNotVisible>1024</memNotVisible>
      <swapVisible>1024</swapVisible>
      <swapNotVisible>1024</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
عدد صحیح مثبتی که نسخه پیکربندی را مشخص می‌کند. این باید 1 باشد.
minimumRequiredMemTotal

حداقل سیستم MemTotal (به مبی‌بایت، شامل کف) موردنیاز برای اعمال این مجموعه محدودیت. این سرویس این مقدار را با کل حافظه دردسترس گزارش‌شده توسط هسته در /proc/meminfo ارزیابی می‌کند.

درک مرزها و بخش‌های اختصاصی حافظه: مقدار هسته MemTotal نشان‌دهنده DRAM فیزیکی قابل‌دسترسی برای سیستم‌عامل پس‌از کسر بخش‌های اختصاصی حافظه سخت‌افزار و سفت‌افزار است. بخش‌های جداشده، RAM را برای قطعات سخت‌افزاری تخصصی مانند GPU، مودم باند پایه، ISP دوربین، محیط‌های اجرای امن، و ناظران ماشین مجازی رزرو می‌کنند و معمولاً بین ۵۰۰ مبی‌بایت و بیش‌از ۲ گیبی‌بایت از حافظه فیزیکی را مصرف می‌کنند.

چون MemTotal کمتر از DRAM فیزیکی تبلیغ‌شده دستگاه است، آستانه‌های minimumRequiredMemTotal شامل حاشیه‌ای برای این بخش‌های جداشده است. برای مثال، پیکربندی‌ای که دستگاه‌های ۱۶ گیگابایتی را هدف قرار می‌دهد، به‌جای 16384 (۱۶ گیگابایت)، حداقل 14336 (۱۴ گیگابایت) را مشخص می‌کند، و رده‌ای برای دستگاه‌های ۱۲ گیگابایتی حداقل 9216 (۹ گیگابایت) را مشخص می‌کند. این کار تضمین می‌کند که هر دستگاه با سطح موردنظر خود مطابقت داشته باشد، صرف‌نظر از تغییرات استثنایی در سطح برد.

maximumAllowedMemTotal

حداکثر MemTotal سیستم (به مبی‌بایت، سقف انحصاری) برای این حد تنظیم شده است تا اعمال شود. اگر MemTotal بزرگ‌تر یا مساوی این سقف باشد، محدودیت تنظیم‌شده اعمال نمی‌شود. برای دستگاه‌های دارای ۱۸٬۴۳۲ مبی‌بایت یا بیشتر یا برای دستگاه‌های دارای کمتر از ۳٬۲۰۰ مبی‌بایت، «محدودکننده حافظه» محدودیتی اعمال نمی‌کند و فرایندها را بدون محدودیت رها می‌کند.

memVisible

محدودیت حافظه نرم (memory.high) در MiB برای فرایندهای قابل‌مشاهده اعمال شد.

memNotVisible

حد نرم حافظه (memory.high) در MiB برای فرایندهای غیرقابل‌مشاهده اعمال شد.

swapVisible

حد فضای مبادله (memory.swap.max) در MiB برای فرایندهای نمایان اعمال شد.

swapNotVisible

حد فضای مبادله (memory.swap.max) در MiB برای فرایندهای غیرقابل‌مشاهده اعمال شد.

حدود استاندارد پلاتفرم

در Android 17 و نسخه‌های بالاتر، پلتفرم محدودیت‌های استاندارد حافظه و مبادله را در /system/etc/memory-limiter-config.xml در سراسر ردیف‌های رایج RAM فیزیکی بااستفاده از فاصله‌های محدوده [floor, ceiling) مشخص می‌کند:

حافظه دسترسی تصادفی فیزیکی هدف محدوده MemTotal [کف، سقف)
(minimumRequiredMemTotal,
maximumAllowedMemTotal)
حد حافظه قابل‌مشاهده
(memVisible)
Not visible memory limit
(memNotVisible)
Visible swap limit
(swapVisible)
Not visible swap limit
(swapNotVisible)
‫۱۶ گیگابایت ‫[14336, 18432) مبی‌بایت ۱۰٬۲۴۰ مبی‌بایت (۱۰ گیبی‌بایت) ‫۵٬۱۲۰ مبی‌بایت (۵ گیبی‌بایت) ‫۵٬۱۲۰ مبی‌بایت (۵ گیبی‌بایت) ‫۵٬۱۲۰ مبی‌بایت (۵ گیبی‌بایت)
‫۱۲ گیگابایت ‫[9216, 14336) مبی‌بایت ‫۸٬۱۹۲ مبی‌بایت (۸ گیبی‌بایت) ‫۴٬۰۹۶ مبی‌بایت (۴ گیبی‌بایت) ‫۴٬۰۹۶ مبی‌بایت (۴ گیبی‌بایت) ‫۴٬۰۹۶ مبی‌بایت (۴ گیبی‌بایت)
‫۸ گیگابایت ‫[6800, 9216) مبی‌بایت ‫۵٬۱۲۰ مبی‌بایت (۵ گیبی‌بایت) ‫۳٬۰۷۲ مبی‌بایت (۳ گیبی‌بایت) ‫۳٬۰۷۲ مبی‌بایت (۳ گیبی‌بایت) ‫۳٬۰۷۲ مبی‌بایت (۳ گیبی‌بایت)
‫۶ گیگابایت ‫[4800, 6800) مبی‌بایت ‫۴٬۰۹۶ مبی‌بایت (۴ گیبی‌بایت) ‫۲٬۰۴۸ مبی‌بایت (۲ گیبی‌بایت) ‫۲٬۰۴۸ مبی‌بایت (۲ گیبی‌بایت) ‫۲٬۰۴۸ مبی‌بایت (۲ گیبی‌بایت)
‫۴ گیگابایت ‫[3200, 4800) مبی‌بایت ‫۲٬۰۴۸ مبی‌بایت (۲ گیبی‌بایت) ‫۱٬۰۲۴ مبی‌بایت (۱ گیبی‌بایت) ‫۱٬۰۲۴ مبی‌بایت (۱ گیبی‌بایت) ‫۱٬۰۲۴ مبی‌بایت (۱ گیبی‌بایت)

اصول محدودیت حافظه

پیکربندی «محدودکننده حافظه» براساس اصول پلاتفرم زیر طراحی شده است:

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

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

    • فرایندهای نمایان: تقریباً ۱/۲ تا ۲/۳ از کل RAM فیزیکی را برای پشتیبانی از رابط کاربری فعال، ترکیب‌بندی، و بارهای کاری پردازشی اختصاص می‌دهد.
    • فرایندهای غیرقابل‌مشاهده: تقریباً ۱/۴ تا ۱/۳ از کل RAM فیزیکی برای کارهای پس‌زمینه اختصاص داده شده است.
  • قابلیت اعمال جهانی: محدودیت‌ها به‌طور یکسان بر همه فرایندهای برنامه در دستگاه (UID >= 10000)، ازجمله برنامه‌های سیستم و OEM ازپیش نصب‌شده، اعمال می‌شود. «محدودکننده حافظه» از فهرست‌های مجاز برای مستثنی کردن برنامه‌های خاص پشتیبانی نمی‌کند تا از مدیریت عادلانه حافظه در همه برنامه‌ها اطمینان حاصل شود. برای پیکربندی کف‌های محافظتی خودتنظیم memory.min در دیمون‌های تعاملی حیاتی و سقف‌های کالیبره‌شده با حجم کاری memory.high سخت‌گیرانه‌تر در برنامه‌ها و سرویس‌های سیستم به‌صورت جداگانه، بودجه‌های حافظه سیستم را ببینید.

  • نبود میاناهای برنامه‌سازی کاربردی پُرسمان زمان اجرا: در Android 17 و بالاتر، برنامه‌ها نمی‌توانند به‌صورت برنامه‌نویسی‌شده محدودیت‌های حافظه اختصاص‌داده‌شده خود را در زمان اجرا پُرسمان کنند. حدود استاندارد پلاتفرم با حاشیه‌های سخاوتمندانه تعریف شده‌اند تا برنامه‌های خوش‌رفتار درطول استفاده عادی بدون محدودیت عمل کنند.

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

فرمان‌های Shell

فرمان am memory-limiter به توسعه‌دهندگان و یکپارچه‌سازان سیستم اجازه می‌دهد در زمان اجرا برای توسعه، آزمایش، و اشکال‌زدایی با سرویس تعامل داشته باشند:

am memory-limiter <SUB-COMMAND>

وضعیت

زیرفرمان status وضعیت عملیاتی و سنجه‌های فعال «محدودکننده حافظه» را گزارش می‌کند:

adb shell am memory-limiter status

در زیر نمونه‌ای از برونداد را می‌بینید:

Memory limiter
  enabled                  monitoring=true          ignored=none
  visibleMem=1948MB        visibleSwap=974MB
  notVisibleMem=974MB      notVisibleSwap=487MB
  started=36               watched=36               watch-failed=0
  events=0                 processes=36             process-hwm=36

فیلدهای کلیدی در برونداد شامل موارد زیر است:

monitoring
نشان می‌دهد که آیا «محدودکننده حافظه» به‌طور فعال فرایندها را تماشا می‌کند یا نه.
‫visibleMem و notVisibleMem
محدودیت‌های حافظه مطلق محاسبه‌شده (memory.high) که درحال‌حاضر برای هر وضعیت رؤیت‌پذیری اعمال می‌شود.
‫visibleSwap و notVisibleSwap
حدود مطلق محاسبه‌شده برای تعویض (memory.swap.max) که درحال‌حاضر برای هر وضعیت رؤیت‌پذیری اعمال می‌شود.
events
تعداد دفعاتی که فرایند از حد تعیین‌شده‌اش فراتر رفته است.
processes
تعداد فعلی فرایندهای تحت نظارت.

صرف‌نظر کردن

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

# Ignore a specific UID
adb shell am memory-limiter ignore 10087
# Ignore all processes (temporarily disables limiting)
adb shell am memory-limiter ignore all
# Resume normal limiting operation
adb shell am memory-limiter ignore none

دستی

زیرفرمان manual حدود محاسبه‌شده برای فرایندی خاص را براساس شناسه فرایند (PID) با مقدار مطلق سفارشی برحسب بایت لغو می‌کند. مقدار باید عدد صحیح باشد، اما می‌تواند شامل پسوند MB برای نشان دادن مبی‌بایت یا پسوند GB برای نشان دادن گیبی‌بایت باشد:

# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1073741824
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1024MB
# Set a 1GiB limit for PID 1234
adb shell am memory-limiter manual 1234 1GB
# Remove the manual override for PID 1234
adb shell am memory-limiter manual 1234 none

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

لغو دستی نمی‌تواند از محدودیت‌های فیزیکی سیستم فراتر رود.