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:
- Anonymous memory check: Evaluates the process's anonymous memory. If it
exceeds the configured
anon_limit_in_mb,pmgdkills the process immediately. - Reclaim wait period: If the anonymous memory is under the specified
anonymous memory limit,
pmgdwaits for a system reclaim grace period (reclaim_wait_time_secs). - Memory evaluation after reclaim: If the target process's
memory.currentremains greater than or equal tomemory.highafter the grace period, or anonymous memory exceedsanon_limit_in_mb,pmgdkills 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,
pmgdtracks process kills in/data/misc/pmgd/history.json. The daemon restricts processes to a singlepmgd-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
}
]
}