Process memory guardian daemon

Android 17 and higher supports the process memory guardian daemon (PMGD), which protects system health and the user experience by proactively managing memory usage on a per-process basis. The daemon improves overall device stability by gracefully enforcing memory ceilings on specific target processes, verifying that isolated memory leaks or spikes don't cause system-wide performance degradation.

While conventional global low-memory killers act only when the entire system is under pressure, PMGD takes a granular approach. The daemon achieves this by monitoring Control Group v2 memory values for its target processes. When a targeted process exceeds its configured memory limits, pmgd handles limit violations by logging Statsd memory atoms before terminating the process.

How it works

The daemon uses inotify to listen for memory pressure events (specifically high-memory activity using memory.events). When a monitored process triggers a memory event, pmgd performs the following actions:

  1. Anonymous memory check: Evaluates the process's anonymous memory. If it exceeds the configured anon_limit_in_mb, pmgd kills the process immediately.
  2. Reclaim wait period: If the anonymous memory is under the specified anonymous memory limit, pmgd waits for a system reclaim grace period (reclaim_wait_time_secs).
  3. Memory evaluation after reclaim: If the target process's memory.current remains greater than or equal to memory.high after the grace period, or anonymous memory exceeds anon_limit_in_mb, pmgd kills the process immediately.

This is done continuously until the process is killed or reclaim on the process brings down its memory usage below the specified memory limits.

System health features

  • Reboot rate limiting: To prevent boot loops or persistent crashes, pmgd tracks process kills in /data/misc/pmgd/history.json. The daemon restricts processes to a single pmgd-initiated kill per device reboot.

SELinux configuration

PMGD's ability to monitor processes is restricted by SELinux policy. If you configure PMGD to monitor a process whose domain isn't allowed by policy, such as a vendor-specific system process, PMGD can't monitor it, and you might see SELinux denials in logcat.

To allow PMGD to monitor processes in additional domains, you must extend PMGD's permissions by updating your device-specific SELinux policy for PMGD.

The following is an example device/<vendor>/<device>/sepolicy/pmgd.te file that adds access to a new domain:

# Allow pmgd to access vendor_system_apps
r_dir_file(pmgd, vendor_system_apps)

For more information about writing device-specific policy, see Implement SELinux.

Vendor-defined configuration

PMGD configuration is vendor driven, configured by a required JSON file /vendor/etc/pmgd/config.json. This lists the processes to track, their configured memory limit profile (using cgroup task profiles), and the hard anonymous memory limit in megabytes.

Vendor configuration fields

The provided JSON configuration is a list of processes and their limits, defined by the following fields:

Field Type Required Description Default
target_cmd String Yes The command name of the target process to monitor, for example, system_server. N/A
uid Integer No The user ID (UID) of the process. If omitted, pmgd applies the rule globally to any process matching target_cmd. N/A
reclaim_wait_time_secs Integer No The grace period in seconds to wait for the system to reclaim memory before evaluating the memory limit again. 5
mem_limit_profile String Yes The name of the cgroup task profile that sets `memory.high`. This is used to set the process memory limit. N/A
anon_limit_in_mb Integer Yes The ultimate anonymous memory limit in megabytes. If the anonymous memory utilization surpasses this value, pmgd promptly kills the process. N/A
additional_task_profiles List of strings No A list of any additional task profiles that pmgd applies to the process when monitoring starts. Empty list

The following is an example configuration of the cgroup task profile in vendor/etc/task_profiles.json:

{
  "Attributes": [
    ...
    {
      "Name": "MemHigh",
      "Controller": "memory",
      "File": "memory.high"
    }
  ],
  "Profiles": [
    {
      "Name": "SystemServerMemoryHighLimit",
      "Actions": [
        {
          "Name": "SetAttribute",
          "Params":
          {
            "Name": "MemHigh",
            "Value": "1080M"
          }
        }
      ]
    }
  ]
}

The following is an example configuration of the PMGD config in vendor/etc/pmgd/config.json:

{
  "targets": [
    {
      "target_cmd": "system_server",
      "uid": 1000,
      "reclaim_wait_time_secs": 5,
      "mem_limit_profile": "SystemServerMemoryHighLimit",
      "anon_limit_in_mb": 300
    }
  ]
}