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

اندروید ۱۷ و بالاتر شامل محدودکننده حافظه است، یک سرویس سیستمی که با استفاده از لینوکس cgroup نسخه ۲، میزان استفاده از حافظه توسط فرآیندهای برنامه را نظارت و محدود می‌کند. محدودکننده حافظه مانع از مصرف نامتناسب حافظه سیستم توسط برنامه‌های جداگانه می‌شود، که فشار کلی بر حافظه را کاهش می‌دهد و از حذف تهاجمی فرآیندهای حیاتی پیش‌زمینه و حافظه پنهان در اثر کمبود حافظه جلوگیری می‌کند.

مکانیسم

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

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

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

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

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

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

در شرایط حاد - مانند نشت حافظه کنترل نشده که در آن برنامه پس از اتمام فضای swap به تخصیص حافظه ناشناس ادامه می‌دهد - برنامه در تخصیص حافظه ناموفق بوده و خاتمه می‌یابد.

نظارت بر فرآیند

محدودکننده حافظه به طور پیش‌فرض فرآیندهای برنامه (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 تعیین‌شده برای خود فراتر می‌رود، Memory Limiter این رویداد را تشخیص می‌دهد و می‌تواند اقدامات تشخیصی مانند ثبت یک پروفایل حافظه یا ثبت یک ناهنجاری در statsd را آغاز کند.

پیکربندی

محدودکننده حافظه، پیکربندی خود را از پارتیشن vendor می‌خواند:

  • مسیر فایل: /vendor/etc/memory-limiter-config.xml

فرمت XML

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

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <!-- RAM minimums account for MemTotal excluding carve-outs. -->
    <limitSet>
      <!-- Limits for 16GB RAM device (MemTotal >= 15GiB): 10G/5G/5G/5G -->
      <minimumRequiredMemTotal>15360</minimumRequiredMemTotal>
      <memVisible>10240</memVisible>
      <memNotVisible>5120</memNotVisible>
      <swapVisible>5120</swapVisible>
      <swapNotVisible>5120</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 12GB RAM device (MemTotal >= 11GiB): 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>11264</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 8GB RAM device (MemTotal >= 7GiB): 5G/3G/3G/3G -->
      <minimumRequiredMemTotal>7168</minimumRequiredMemTotal>
      <memVisible>5120</memVisible>
      <memNotVisible>3072</memNotVisible>
      <swapVisible>3072</swapVisible>
      <swapNotVisible>3072</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 6GB RAM device (MemTotal >= 5GiB): 4G/2G/2G/2G -->
      <minimumRequiredMemTotal>5120</minimumRequiredMemTotal>
      <memVisible>4096</memVisible>
      <memNotVisible>2048</memNotVisible>
      <swapVisible>2048</swapVisible>
      <swapNotVisible>2048</swapNotVisible>
    </limitSet>
    <limitSet>
      <!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
      <minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
      <memVisible>2048</memVisible>
      <memNotVisible>1024</memNotVisible>
      <swapVisible>1024</swapVisible>
      <swapNotVisible>1024</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
یک عدد صحیح مثبت که نسخه پیکربندی را مشخص می‌کند. این عدد باید 1 باشد.
minimumRequiredMemTotal

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

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

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

memVisible

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

memNotVisible

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

swapVisible

محدودیت فضای swap ( memory.swap.max ) بر حسب مگابایت که برای فرآیندهای قابل مشاهده اعمال می‌شود.

swapNotVisible

محدودیت فضای swap ( memory.swap.max ) بر حسب مگابایت برای فرآیندهای غیرقابل مشاهده اعمال می‌شود.

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

در اندروید ۱۷ و بالاتر، پلتفرم محدودیت‌های استاندارد حافظه و فضای ذخیره‌سازی را در /vendor/etc/memory-limiter-config.xml در بین لایه‌های رم فیزیکی رایج مشخص می‌کند:

رم فیزیکی حداقل مورد نیاز
مم توتال
(حداقل مقدار مورد نیاز)
محدودیت حافظه قابل مشاهده
(memVisible)
محدودیت حافظه قابل مشاهده نیست
(memNotVisible)
محدودیت سوآپ قابل مشاهده
(قابل تعویض)
محدودیت سوآپ قابل مشاهده نیست
(swapNotVisible)
۱۶ گیگابایت ۱۵,۳۶۰ مگابایت (۱۵ گیگابایت) ۱۰،۲۴۰ مگابایت (۱۰ گیگابایت) ۵،۱۲۰ مگابایت (۵ گیگابایت) ۵،۱۲۰ مگابایت (۵ گیگابایت) ۵،۱۲۰ مگابایت (۵ گیگابایت)
۱۲ گیگابایت ۱۱,۲۶۴ مگابایت (۱۱ گیگابایت) ۸,۱۹۲ مگابایت (۸ گیگابایت) ۴،۰۹۶ مگابایت (۴ گیگابایت) ۴،۰۹۶ مگابایت (۴ گیگابایت) ۴،۰۹۶ مگابایت (۴ گیگابایت)
۸ گیگابایت ۷,۱۶۸ مگابایت (۷ گیگابایت) ۵،۱۲۰ مگابایت (۵ گیگابایت) ۳،۰۷۲ مگابایت (۳ گیگابایت) ۳،۰۷۲ مگابایت (۳ گیگابایت) ۳،۰۷۲ مگابایت (۳ گیگابایت)
۶ گیگابایت ۵،۱۲۰ مگابایت (۵ گیگابایت) ۴،۰۹۶ مگابایت (۴ گیگابایت) ۲،۰۴۸ مگابایت (۲ گیگابایت) ۲،۰۴۸ مگابایت (۲ گیگابایت) ۲،۰۴۸ مگابایت (۲ گیگابایت)
۴ گیگابایت ۳،۰۷۲ مگابایت (۳ گیگابایت) ۲،۰۴۸ مگابایت (۲ گیگابایت) ۱,۰۲۴ مگابایت (۱ گیگابایت) ۱,۰۲۴ مگابایت (۱ گیگابایت) ۱,۰۲۴ مگابایت (۱ گیگابایت)

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

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

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

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

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

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

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

دستورات پوسته

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

am memory-limiter <SUB-COMMAND>

وضعیت

زیردستور status وضعیت عملیاتی و معیارهای فعال Memory Limiter را گزارش می‌دهد:

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
محدودیت‌های مطلق محاسبه‌شده‌ی swap ( 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

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

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