فریزر برنامه های ذخیره شده

اندروید ۱۱ (سطح API 30) یا بالاتر از قابلیت فریز کردن برنامه‌های ذخیره‌شده در حافظه پنهان پشتیبانی می‌کند. این ویژگی با جلوگیری از عملکرد نادرست برنامه‌هایی که ممکن است در حین ذخیره شدن در حافظه پنهان تلاش به اجرا داشته باشند، اجرای فرآیندهای ذخیره‌شده در حافظه پنهان را متوقف کرده و میزان استفاده از منابع را کاهش می‌دهد.

قابلیت فریز کردن برنامه‌های ذخیره‌شده، برنامه‌ها را در حافظه رم نگه می‌دارد و در عین حال آنها را از پردازنده دور نگه می‌دارد. اگر اندروید تشخیص دهد که یک برنامه نباید کار کند اما ممکن است در آینده مورد نیاز باشد، به جای خاتمه دادن به آن، فرآیند برنامه را فریز می‌کند. این کار از شروع سرد (cold start) در زمانی که دوباره به برنامه نیاز است، جلوگیری می‌کند.

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

در اندروید ۱۴ (سطح API ۳۴) و بالاتر، فریزر برنامه‌های ذخیره‌شده شامل رفتارهای قوی زیر است:

  • فرآیندهای برنامه در حالت ذخیره شده، 10 ثانیه پس از ورود به حالت ذخیره شده، مسدود می‌شوند.
  • سیستم بلافاصله در طول یک رویداد چرخه عمر، فرآیند برنامه‌ی قفل‌شده را از حالت قفل‌شده خارج می‌کند. این رویدادها شامل دریافت یک intent ، شروع یک سرویس کاری یا از سرگیری یک فعالیت توسط کاربر می‌شود.

ActivityManagerService تمام فرآیندهای برنامه را مدیریت می‌کند و تصمیمات مربوط به چرخه عمر برنامه را می‌گیرد. CachedAppOptimizer مسئول فریز کردن فرآیند برنامه است.

وقتی یک فرآیند برنامه‌ای قفل می‌شود، تمام نخ‌های آن به حالت تعلیق در می‌آیند و تا زمانی که قفل نشوند، نمی‌توانند کار CPU را انجام دهند. در نتیجه، برنامه نمی‌تواند جمع‌آوری زباله (GC) را انجام دهد و نمی‌تواند به رویدادهای اصلاح حافظه پاسخ دهد. برای جزئیات بیشتر، به ComponentCallbacks2.onTrimMemory(int) مراجعه کنید. برای تطبیق با این مورد، از اندروید ۱۴ شروع کنید:

  • برنامه‌هایی که نمونه Activity قابل مشاهده‌ای دارند، به محض انتقال به پس‌زمینه، از TRIM_MEMORY_UI_HIDDEN مطلع می‌شوند. برنامه‌هایی که بدون UI در چرخه عمر باقی می‌مانند، مانند برنامه‌هایی با سرویس پیش‌زمینه، ممکن است TRIM_MEMORY_BACKGROUND دریافت کنند. سایر رویدادهای trim ارائه نمی‌شوند، زیرا وقتی برنامه‌ها واجد شرایط آن رویدادها هستند، انتظار می‌رود که منجمد شوند.
  • کمی پس از ورود به حالت ذخیره شده، سیستم ممکن است از برنامه در زمان اجرا درخواست کند تا یک GC را برای آماده سازی جهت جلوگیری از مسدود شدن احتمالی انجام دهد.
  • وقتی یک فرآیند برنامه‌ای متوقف می‌شود، ممکن است مراحل فشرده‌سازی حافظه اضافی، مانند نوشتن صفحات کثیف در حافظه پشتیبان و تعویض صفحات ناشناس به ZRAM، رخ دهد.
  • اگر تمام فرآیندهای یک برنامه خاص مسدود شوند، سیستم هرگونه سوکت TCP فعالی که توسط برنامه نگهداری می‌شود را خاتمه می‌دهد. این کار مانع از ارسال پینگ‌های TCP keepalive توسط سرور سوکت می‌شود که مودم دستگاه را از حالت آماده به کار خارج می‌کند.

فرآیندهای برنامه ذخیره شده در حافظه پنهان (Cache) زمانی که وضعیت فرآیند آنها از حالت ذخیره شده به حالت با اهمیت بالاتر ارتقا می‌یابد، از حالت انجماد خارج می‌شوند. برای کاهش رویدادهای انجمادزدایی در اندروید ۱۴ و بالاتر، سیستم، پخش‌های ثبت شده در متن را در حالی که برنامه در حالت ذخیره شده است، در صف قرار می‌دهد. پخش‌های ثبت شده در متن، گیرنده‌هایی هستند که یک برنامه با فراخوانی Context.registerReceiver به صورت پویا ثبت می‌کند. سیستم این پخش‌های صف‌بندی شده را تنها پس از انجمادزدایی برنامه ارائه می‌دهد. در مقابل، سیستم پخش‌های اعلام شده در مانیفست را در صف قرار نمی‌دهد. پخش‌های اعلام شده در مانیفست، گیرنده‌هایی هستند که به صورت ایستا در AndroidManifest.xml با استفاده از عنصر <receiver> اعلام می‌شوند. سیستم بلافاصله برنامه ذخیره شده را برای ارائه پخش‌های اعلام شده در مانیفست از حالت انجماد خارج می‌کند.

تأثیر بر سلامت سیستم

اگر تعداد فرآیندهای برنامه ذخیره شده در حافظه پنهان (cache) بیش از MAX_CACHED_PROCESSES باشد، اندروید فرآیند برنامه ذخیره شده با کمترین استفاده اخیر را خاتمه می‌دهد. در دستگاه‌های پشتیبانی شده که از اندروید ۱۴ یا بالاتر استفاده می‌کنند، MAX_CACHED_PROCESSES به طور قابل توجهی افزایش یافته است و به دستگاه‌ها اجازه می‌دهد فرآیندهای برنامه ذخیره شده بیشتری را در رم نگه دارند.

نگهداری تعداد بیشتری از برنامه‌ها در حافظه رم، تا 30 درصد کاهش در شروع سرد (clad start) را به همراه دارد، که این کاهش‌ها بر اساس کل حافظه رم دستگاه تنظیم می‌شوند. در عین حال، مصرف CPU توسط برنامه‌های ذخیره شده در حافظه رم به حداقل می‌رسد و منجر به صرفه‌جویی قابل توجه در مصرف باتری می‌شود.

معافیت‌های فریزر

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

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

پیاده‌سازی فریزر برنامه‌ها

قابلیت ذخیره‌سازی برنامه‌های ذخیره‌شده (cacheed apps freeze) از قابلیت ذخیره‌سازی cgroup v2 استفاده می‌کند. دستگاه‌هایی که با یک هسته سازگار عرضه می‌شوند می‌توانند آن را فعال کنند. گزینه توسعه‌دهنده Suspend execution for cached apps را فعال کنید یا پرچم پیکربندی دستگاه activity_manager_native_boot use_freezer روی true تنظیم کنید. برای مثال:

adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot

وقتی پرچم use_freezer روی false تنظیم کنید یا گزینه توسعه‌دهنده را غیرفعال کنید، فریزر غیرفعال می‌شود. برای مثال:

adb shell device_config put activity_manager_native_boot use_freezer false && adb reboot

می‌توانید با تغییر پیکربندی دستگاه در نسخه یا به‌روزرسانی نرم‌افزار، این تنظیم را تغییر دهید.

برای لغو MAX_CACHED_PROCESSES ، به عنوان مثال، برای تنظیم مقدار روی ۱۰۲۴ برای آزمایش:

adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistent

برای برگرداندن لغو MAX_CACHED_PROCESSES :

adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests none

فریزر برنامه‌ها، APIهای رسمی را افشا نمی‌کند و کلاینت پیاده‌سازی مرجع ندارد، اما از APIهای سیستمی مخفی setProcessFrozen برای فریز کردن یک فرآیند خاص و enableFreezer برای فعال یا غیرفعال کردن فریز کردن سراسری استفاده می‌کند.

مدیریت ویژگی‌های سفارشی

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

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

حالت‌های خرابی رایج

وقتی فرآیندهای برنامه هنگ می‌کنند، ارتباط بین فرآیندی (IPC) یا زمان‌بندی نامناسب وظایف می‌تواند منجر به خاتمه برنامه یا رفتار غیرمنتظره شود.

تراکنش‌های اتصال همزمان به فرآیندهای منجمد

وقتی یک فرآیند برنامه کلاینت، یک تراکنش اتصال همزمان را به یک فرآیند برنامه سرور که مسدود شده است ارسال می‌کند، سیستم بلافاصله فرآیند برنامه سرور را خاتمه می‌دهد. این کار مانع از مسدود شدن نامحدود نخ کلاینت در حین انتظار برای پاسخ از سرور مسدود شده می‌شود. سپس نخ کلاینت RemoteException دریافت می‌کند و هر شنونده ثبت شده‌ای فعال می‌شود. برای جزئیات بیشتر، به IBinder.linkToDeath مراجعه کنید.

علت اصلی: این خرابی معمولاً ناشی از یک اشکال در برنامه کلاینت است. وقتی یک کلاینت به یک سرویس متصل می‌شود، فرآیند سرور به کلاینت متصل می‌شود و از ورود به حالت کش شده قبل از کلاینت جلوگیری می‌شود. برای جزئیات بیشتر، به Context.bindService مراجعه کنید. با این حال، هنگامی که کلاینت Context.unbindService را فراخوانی می‌کند، فرآیند سرور می‌تواند کش شده و منجمد شود. اگر کلاینت پس از unbinding به استفاده از مرجع IBinder کش شده ادامه دهد، خطر برقراری ارتباط با یک فرآیند منجمد وجود دارد.

برای جلوگیری از این مشکل، مطمئن شوید که برنامه‌های کلاینت بلافاصله پس از فراخوانی Context.unbindService ارجاعات IBinder را حذف می‌کنند.

سرریز بافر تراکنش اتصال‌دهنده ناهمزمان

وقتی یک فرآیند برنامه‌ی سرور در حالت فریز شده، تراکنش‌های اتصال ناهمزمان ( oneway ) دریافت می‌کند، این تراکنش‌ها در یک بافر به ازای هر فرآیند بافر می‌شوند. اگر سرور در حالت فریز شده، تراکنش‌های ناهمزمان زیادی دریافت کند، بافر سرریز می‌شود و سیستم، فرآیند برنامه‌ی سرور را خاتمه می‌دهد.

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

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

اگر یک برنامه وظایف تکراری را اجرا کند، آنها در حالی که فرآیند متوقف می‌شود، به حالت تعلیق در می‌آیند. برای جزئیات بیشتر، به ScheduledThreadPoolExecutor.scheduleAtFixedRate یا Timer.scheduleAtFixedRate مراجعه کنید. هنگامی که فرآیند از حالت توقف خارج می‌شود، اجراهای از دست رفته انباشته شده ممکن است به سرعت و تقریباً بدون هیچ تأخیری پشت سر هم اجرا شوند.

برای جلوگیری از افزایش ناگهانی تعداد اجراها هنگام از کار افتادن برنامه، برای وظایف پس‌زمینه به جای scheduleAtFixedRate از scheduleWithFixedDelay استفاده کنید. همچنین می‌توانید WorkManager استفاده کنید.

فریزر برنامه‌ها را آزمایش و عیب‌یابی کنید

برای تأیید عملکرد صحیح فریزر برنامه یا عیب‌یابی مشکلات مربوط به فریزر، از ابزارها و دستورات تشخیصی زیر استفاده کنید:

دستورات مدیریت فعالیت

شما می‌توانید از دستورات adb shell am برای کنترل دستی فریز کردن و فشرده‌سازی برای یک فرآیند خاص استفاده کنید:

  • مجبور کردن یک فرآیند به فریز کردن:

    adb shell am freeze <process>
  • مجبور کردن یک فرآیند به خارج شدن از حالت فریز:

    adb shell am unfreeze <process>
  • فشرده‌سازی کامل حافظه را روی یک فرآیند اعمال کنید:

    adb shell am compact full <process>

معاینه لاگات

برای مشاهده‌ی ورودی‌های فریز شده و نشده در هر بار مهاجرت یک فرآیند به داخل یا خارج از فریز، دستور logcat را اجرا کنید:

adb logcat | grep -i "\(freezing\|froze\)"

لاگ‌های دلیلِ رفع انجماد، مقادیر شمارش‌شده را از بافر شمارشی پروتکل UnfreezeReason خروجی می‌دهند.

بازرسی دامپسی

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

adb shell dumpsys activity | grep -A 20 "Apps frozen:"

وجود فایل /sys/fs/cgroup/uid_0/cgroup.freeze را بررسی کنید.

اطلاعات خروج از برنامه

برای جستجوی دلیل خاتمه یک فرآیند قبلی، به ActivityManager.getHistoricalProcessExitReasons مراجعه کنید. اگر یک فرآیند برنامه به دلیل مشکلی مرتبط با فریز شدن، مانند دریافت یک تراکنش binder همزمان در حین فریز شدن، خاتمه یافته باشد، دلیل خروج روی ApplicationExitInfo.REASON_FREEZER تنظیم می‌شود.

ردیابی کامل

رویدادهای مرتبط با فریزر در مسیرهای Perfetto به مسیری به نام Freezer تحت فرآیند system_server ارسال می‌شوند:

  • برش‌های Freeze و Unfreeze نشان می‌دهند که چه زمانی یک فرآیند تغییر حالت می‌دهد.
  • رویدادهای updateAppFreezeStateLSP نشان می‌دهند که چه زمانی سرور سیستم، ویژگی‌های فرآیند را برای تصمیم‌گیری در مورد فریز کردن یا آزاد کردن فریز، دوباره بررسی می‌کند.

شما می‌توانید این رویدادها را مستقیماً در رابط کاربری Perfetto بررسی کنید یا آنها را با استفاده از PerfettoSQL تجزیه و تحلیل کنید:

INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");

در کتابخانه استاندارد PerfettoSQL، رویدادهای freezer نیز در جدول android_freezer_events خلاصه می‌شوند.