Cached apps freezer

Android 11 (API level 30) or higher supports the cached apps freezer. This feature stops execution for cached processes and reduces resource usage by misbehaving apps that might attempt to operate while cached.

The cached apps freezer keeps apps in RAM while keeping them off the CPU. If Android determines that an app shouldn't be doing work but might be needed in the future, it freezes the app process rather than terminating it. This prevents a cold start when the app is needed again.

Android freezes cached apps by migrating their processes into a frozen cgroup. This reduces active and idle CPU consumption in the presence of active cached apps. You can enable the app freezer using a system configuration flag or a developer option.

In Android 14 (API level 34) and higher, the cached apps freezer includes the following robust behaviors:

  • App processes in the cached state are frozen 10 seconds after entering the cached state.
  • The system immediately unfreezes a frozen app process during a lifecycle event. These events include receiving an intent, starting a job service, or the user resuming an activity.

ActivityManagerService manages all app processes and makes app lifecycle decisions. CachedAppOptimizer is responsible for freezing the app process.

When an app process is frozen, all of its threads are suspended and can't perform CPU work until unfrozen. As a result, the app can't perform garbage collection (GC) and can't respond to memory trim events. For details, see ComponentCallbacks2.onTrimMemory(int). To accommodate this, starting in Android 14:

  • Apps with a visible Activity instance are notified of TRIM_MEMORY_UI_HIDDEN as soon as they move to the background. Apps that stay in a lifecycle without a UI, such as apps with a foreground service, might receive TRIM_MEMORY_BACKGROUND. Other trim events aren't delivered, because when apps are eligible for those events, they're expected to be frozen.
  • Shortly after entering the cached state, the system might request the app runtime to perform a GC in preparation for potentially becoming frozen.
  • When an app process is frozen, additional memory compaction steps might occur, such as writing dirty pages to backing storage and swapping anonymous pages to ZRAM.
  • If all processes for a particular app are frozen, the system terminates any active TCP sockets maintained by the app. This prevents the server side of the socket from sending TCP keepalive pings that would wake up the device modem.

Cached app processes are unfrozen when their process state elevates from cached to a higher importance state. To reduce unfreeze events in Android 14 and higher, the system queues context-registered broadcasts while the app is in the cached state. Context-registered broadcasts are receivers that an app registers dynamically by calling Context.registerReceiver. The system delivers these queued broadcasts only after the app is unfrozen. In contrast, the system does not queue manifest-declared broadcasts. Manifest-declared broadcasts are receivers statically declared in AndroidManifest.xml using the <receiver> element. The system immediately unfreezes the cached app to deliver manifest-declared broadcasts.

System health impact

Android terminates the least recently used cached app process if there are more than MAX_CACHED_PROCESSES cached app processes. On supported devices running Android 14 or higher, MAX_CACHED_PROCESSES is significantly increased, allowing devices to maintain substantially more cached app processes in RAM.

Maintaining more apps cached in RAM yields up to a 30% reduction in cold starts, with reductions scaling based on total device RAM. At the same time, CPU consumption by cached apps is minimized, leading to significant battery savings.

Freezer exemptions

Under certain conditions, an app process might enter the cached state but remain unfrozen. These exemptions are implementation details and might change in future Android versions:

  • File locks: If a cached process holds a file lock that blocks other noncached processes, the process holding the lock isn't frozen.
  • BIND_WAIVE_PRIORITY bindings: App processes with incoming bindings created using Context.BIND_WAIVE_PRIORITY can enter the cached state but remain unfrozen until all connected client processes are also cached. This exemption supports multi-process apps, such as web browsers using Custom Tabs.

Implement the apps freezer

The cached apps freezer leverages the kernel cgroup v2 freezer. Devices shipping with a compatible kernel can enable it. Enable the developer option Suspend execution for cached apps or set the device config flag activity_manager_native_boot use_freezer to true. For example:

adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot

The freezer is disabled when you set the flag use_freezer to false or disable the developer option. For example:

adb shell device_config put activity_manager_native_boot use_freezer false && adb reboot

You can toggle this setting by changing a device configuration in a software release or update.

To override MAX_CACHED_PROCESSES, for example, to set the value to 1024 for testing:

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

To revert the MAX_CACHED_PROCESSES override:

adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests none

The apps freezer doesn't expose official APIs and doesn't have a reference implementation client, but it does use the hidden system APIs setProcessFrozen to freeze an individual process and enableFreezer to enable or disable freezing globally.

Handle custom features

App processes aren't expected to do any work when cached, but some apps might have custom features supported by processes that are expected to run while cached. When the apps freezer is enabled on a device running such apps, the cached processes are frozen and might prevent custom features from working.

As a workaround, you can change the process status to noncached before the process needs to do any work. This change allows apps to remain active. Examples of active statuses include a bound foreground service or foreground status.

Common failure modes

When app processes are frozen, improper inter-process communication (IPC) or task scheduling can lead to app terminations or unexpected behavior.

Synchronous binder transactions to frozen processes

When a client app process sends a synchronous binder transaction to a server app process that is frozen, the system immediately terminates the server app process. This prevents the client thread from blocking indefinitely while waiting for a response from the frozen server. The client thread then receives RemoteException, and any registered listeners are triggered. For details, see IBinder.linkToDeath.

Root cause: This failure is typically caused by a bug in the client app. When a client binds to a service, the server process is bound to the client and is prevented from entering the cached state before the client does. For details, see Context.bindService. However, once the client calls Context.unbindService, the server process can become cached and frozen. If the client continues to use the cached IBinder reference after unbinding, it risks communicating with a frozen process.

To avoid this issue, ensure client apps discard IBinder references immediately after calling Context.unbindService.

Asynchronous binder transaction buffer overflow

When a server app process receives asynchronous (oneway) binder transactions while frozen, the transactions are buffered in a per-process buffer. If the server receives too many asynchronous transactions while frozen, the buffer overflows, and the system terminates the server app process.

To prevent this buffer overflow, avoid sending excessive asynchronous binder transactions to processes that might be cached or frozen.

Repeated execution of scheduled tasks upon unfreezing

If an app executes repetitive tasks, they're suspended while the process is frozen. For details, see ScheduledThreadPoolExecutor.scheduleAtFixedRate or Timer.scheduleAtFixedRate. When the process unfreezes, the accumulated missed executions might run rapidly back-to-back with virtually no delay.

To prevent a surge of executions when the app unfreezes, use scheduleWithFixedDelay instead of scheduleAtFixedRate for background tasks. You can also use WorkManager.

Test and troubleshoot the apps freezer

To verify the app freezer is working as intended or to troubleshoot freezer-related issues, use the following diagnostic tools and commands:

Activity manager commands

You can use adb shell am commands to manually control freezing and compaction for a specific process:

  • Force a process to freeze:

    adb shell am freeze <process>
  • Force a process to unfreeze:

    adb shell am unfreeze <process>
  • Force a full memory compaction on a process:

    adb shell am compact full <process>

Logcat examination

View logcat to see frozen and unfrozen entries each time a process migrates in or out of the freezer:

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

Unfreeze reason logs output enumerated values from the UnfreezeReason protocol buffer enum.

Dumpsys inspection

Check for a list of frozen processes using dumpsys activity:

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

Check for the presence of the /sys/fs/cgroup/uid_0/cgroup.freeze file.

ApplicationExitInfo

To query the reason for a previous process termination, see ActivityManager.getHistoricalProcessExitReasons. If an app process was terminated due to a freezer-related issue, such as receiving a synchronous binder transaction while frozen, the exit reason is set to ApplicationExitInfo.REASON_FREEZER.

Perfetto tracing

Freezer-related events are emitted to a track named Freezer under the system_server process in Perfetto traces:

  • Freeze and Unfreeze slices indicate when a process changes state.
  • updateAppFreezeStateLSP events show when the system server re-examines process attributes to make freezing or unfreezing decisions.

You can inspect these events directly in the Perfetto UI or analyze them using 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 %");

In the PerfettoSQL standard library, freezer events are also summarized in the android_freezer_events table.