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

‫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 (در MiB، شامل کف) موردنیاز برای اعمال این مجموعه محدودیت. این سرویس آن را درمقایسه با کل حافظه دردسترس گزارش‌شده توسط هسته در /proc/meminfo ارزیابی می‌کند.

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

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

maximumAllowedMemTotal

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

memVisible

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

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)
محدودیت حافظه غیرقابل‌رؤیت
(memNotVisible)
محدودیت جابه‌جایی رؤیت‌پذیر
(swapVisible)
محدودیت مبادله غیرقابل‌رؤیت
(swapNotVisible)
‫۱۶ گیگابایت ‫[14336, 18432) مبی‌بایت ‫۱۰۲۴۰ مگابایت (۱۰ گیگابایت) ‫۵٬۱۲۰ مبی‌بایت (۵ گیبی‌بایت) ‫۵٬۱۲۰ مگابایت (۵ گیگابایت) ‫۵٬۱۲۰ مگابایت (۵ گیگابایت)
‫۱۲ گیگابایت ‫[9216, 14336) مبی‌بایت ‫۸٬۱۹۲ مبی‌بایت (۸ گیبی‌بایت) ‫۴٬۰۹۶ مبی‌بایت (۴ گیبی‌بایت) ‫۴٬۰۹۶ مبی‌بایت (۴ گیبی‌بایت) ‫۴۰۹۶ مگابایت (۴ گیگابایت)
‫۸ گیگابایت ‫[6800, 9216) مبی‌بایت ‫۵٬۱۲۰ مگابایت (۵ گیگابایت) ‫۳٬۰۷۲ مبی‌بایت (۳ گیبی‌بایت) ‫۳۰۷۲ مگابایت (۳ گیگابایت) ‫۳٬۰۷۲ مبی‌بایت (۳ گیبی‌بایت)
‫۶ گیگابایت ‫[4800, 6800) مبی‌بایت ‫۴۰۹۶ مگابایت (۴ گیگابایت) ‫۲٬۰۴۸ مگابایت (۲ گیگابایت) ‫۲٬۰۴۸ مگابایت (۲ گیگابایت) ‫۲٬۰۴۸ مگابایت (۲ گیگابایت)
‫۴ گیگابایت ‫[3200, 4800) مبی‌بایت ‫۲٬۰۴۸ مگابایت (۲ گیگابایت) ‫۱٬۰۲۴ مبی‌بایت (۱ گیبی‌بایت) ‫۱٬۰۲۴ مگابایت (۱ گیگابایت) ‫۱٬۰۲۴ مبی‌بایت (۱ گیبی‌بایت)

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

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

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

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

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

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

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

فرمان‌های پوسته

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

# 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 برای نشان دادن MiB یا پسوند GB برای نشان دادن GiB را داشته باشد:

# 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

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

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