Android 17 memory limits: 2048 MiB on a 6 GB phone, and your foreground service gets half

Android 17's Memory Limiter caps app RAM by device tier, and foreground services land in the restrictive class.

Read time
13 min
Word count
2.2K
Sections
11
FAQs
8
Share
Android 17 Memory Limiter per-RAM-tier caps for visible and background app processes
Android 17 enforces per-app memory caps that scale with device RAM.
On this page · 11 sections
  1. What the two Google documents actually say
  2. The per-tier caps AOSP publishes
  3. The foreground service trap
  4. How to tell if this already happened to your users
  5. How to test before your users do
  6. What actually changes in the build
  7. India-specific considerations
  8. What is still unknown
  9. FAQ
  10. How eCorpIT can help
  11. References

Summary. Android 17 (API level 37) enforces per-app memory caps that scale with device RAM. The Android Open Source Project publishes the actual numbers, and the Android Developers Blog does not: on a 6 GB device the cap is 2048 MiB for a visible process and 1024 MiB for a background one, with a matching 2048 MiB and 1024 MiB swap ceiling. On a 16 GB device it is 10240 MiB visible and 5120 MiB background. The two Google documents also disagree on what happens when you cross the line. The blog post of 2 June 2026 says Android "will kill the process with no associated stack trace." The AOSP Memory Limiter page says memory.high is a soft limit, the kernel throttles the process and reclaims pages, and the app "continues to run" until swap is exhausted. The gap matters, because the mitigation for a hard kill is different from the mitigation for a throttle. And one detail buried in the AOSP process-state table will surprise most Android teams: FOREGROUND_SERVICE is classified as "Not visible", so a media player or a sync service running behind a notification gets the smaller cap, not the larger one. IDC put India's smartphone average selling price at a record US$302 in Q1 2026, up 10.4% year over year, with the sub-US$100 band down 59% and its share falling from 18% to 8% as memory costs climbed. India's install base skews to exactly the 4 GB and 6 GB tiers where these caps bite hardest.

What the two Google documents actually say

The Android Developers Blog published "Prioritizing Memory Efficiency: Essential Steps for Android 17" on 2 June 2026, written by Alice Yuan, Ajesh Pai and Fung Lam of Developer Relations. Its framing is blunt: "To ensure device stability, starting in Android 17, the system will begin enforcing app memory limits based on the device's total RAM. If an app exceeds those limits, Android will kill the process with no associated stack trace."

The AOSP Memory Limiter page describes the same feature at the kernel level and reads differently. The Memory Limiter is a system service that uses Linux cgroup v2. It relies on two attributes: memory.high, described as "A soft limit. When exceeded, the process is throttled and the kernel attempts proactive page reclaim from that process", and memory.swap.max, which bounds ZRAM usage. The AOSP text then says that when an app crosses memory.high, the kernel evicts clean file-backed pages and swaps out inactive anonymous memory, and "the app might experience temporary execution throttling, but it continues to run." Termination is reserved for the extreme case: "an uncontrolled memory leak where the app continues allocating anonymous memory after swap space is exhausted."

Both statements can be true. The blog describes the endpoint your crash dashboard sees; AOSP describes the mechanism that precedes it. But if you plan only for the kill, you will miss the more common outcome, which is an app that stays alive and gets slower. Jank that appears only on 4 GB and 6 GB devices, only after the user has been in the app for a while, is the signature.

The per-tier caps AOSP publishes

The platform default lives in /system/etc/memory-limiter-config.xml, with an optional vendor override at /vendor/etc/memory-limiter-config.xml. The service reads the override first and falls back to the system file. Limit sets are selected by minimumRequiredMemTotal, evaluated against MemTotal in /proc/meminfo, and every value is in mebibytes.

Target physical RAM minimumRequiredMemTotal Visible memory cap Background memory cap Swap cap (visible / background)
16 GB 13312 MiB 10240 MiB 5120 MiB 5120 / 5120 MiB
12 GB 10240 MiB 8192 MiB 4096 MiB 4096 / 4096 MiB
8 GB 6656 MiB 5120 MiB 3072 MiB 3072 / 3072 MiB
6 GB 4608 MiB 4096 MiB 2048 MiB 2048 / 2048 MiB
4 GB 3072 MiB 2048 MiB 1024 MiB 1024 / 1024 MiB

The thresholds sit well below the advertised RAM on purpose. AOSP explains that MemTotal reflects DRAM left after hardware carve-outs for the GPU, baseband modem, camera ISP, secure execution environments and hypervisors, "typically consuming between 500 MiB and over 1 GiB of physical memory." So the 16 GB tier asks for 13312 MiB rather than 16384 MiB, and the 12 GB tier asks for 10240 MiB. A device that ships with 8 GB but carves out 1.5 GB can fall through to the 6 GB limit set. Do not assume the marketing spec picks the tier.

Because a vendor file overrides the system file, the caps on a specific handset are a device-level fact, not a platform-level one. Test on the OEM hardware your users actually carry.

The foreground service trap

AOSP maps every process state to a limit class. The mapping is not the one most Android teams would guess.

Process state Limit class applied
TOP, BOUND_TOP, TOP_SLEEPING Visible
IMPORTANT_FOREGROUND Visible
FOREGROUND_SERVICE, BOUND_FOREGROUND_SERVICE Not visible
SERVICE, RECEIVER, BACKUP, HEAVY_WEIGHT Not visible
HOME, LAST_ACTIVITY, TRANSIENT_BACKGROUND Not visible
CACHED_ACTIVITY, CACHED_RECENT, CACHED_EMPTY Cached (frozen, then maximally reclaimed)
PERSISTENT, PERSISTENT_UI Unrestricted

FOREGROUND_SERVICE sits in the restrictive class. On a 6 GB phone that is 2048 MiB, not 4096 MiB. The reasoning is in the blog post: the whole point of the feature is that an app "holding a privileged state (e.g. it's running a Foreground Service)" was previously shielded from the Low Memory Killer while it hoarded RAM, forcing the LMK to kill dozens of well-behaved cached apps instead. Android 17 closes that shield.

The apps most exposed are the ones that do heavy work behind a notification: audio and video playback with large decode buffers, offline map rendering, camera and media export pipelines, background data synchronization, and anything doing on-device inference outside a visible activity. If you have moved model execution into a foreground service, read the tier numbers again with your peak resident set in hand. Our own reading of the on-device versus cloud AI decision for mobile apps already pushed teams to size the memory budget first; Android 17 turns that budget into an enforced ceiling on the small tiers.

Note also that PERSISTENT and PERSISTENT_UI are unrestricted, and AOSP says the Memory Limiter monitors processes with UID >= 10000 by default, exempting core system processes. Your app is not in that exempt set.

How to tell if this already happened to your users

There is no crash and no stack trace, so the signal is in exit reasons rather than in your crash reporter's default view. Both Google documents give the same detection recipe. Call getDescription() on `ApplicationExitInfo`. If the Memory Limiter applied a limit, the exit reason is REASON_OTHER and the description string contains MemoryLimiter:AnonSwap.

That exact string is the thing to grep for. An exit bucketed as REASON_OTHER with no further parsing is invisible in most dashboards, which is how a memory-limit termination gets filed as "user closed the app."

For heap evidence, Android 17 adds two ProfilingManager triggers on top of the API introduced in Android 15. TRIGGER_TYPE_OOM captures a Java heap dump at the moment an OutOfMemoryError is thrown and hands you the profile on the next app start, once you register registerForAllProfilingResults. TRIGGER_TYPE_ANOMALY fires on breached memory thresholds and delivers a heap dump immediately before the system terminates the app. Analyse the result in the Perfetto Heap Dump Explorer, which is now the default heap-dump view. Separately, AOSP notes that a memory.high breach can log an anomaly to statsd, and Google says Play Console will surface more in-field memory metrics, without naming a date.

How to test before your users do

The Android 17 behavior-changes documentation adds three am subcommands, reachable over adb, that let you impose or lift the constraint on a real device. They do nothing on a device that imposes no limits.


            am memory-limiter ignore <uid>|none|all
am memory-limiter manual <pid> <limit>|max|none
am memory-limiter status
          

ignore takes a UID and exempts every process under it, or all to exempt everything, or none to clear previous exemptions. manual takes a PID and an integer number of megabytes, so passing 30 pins that process to 30 MB; max removes all limits on it and none restores the system default. status reports the limits currently applied to visible and non-visible processes.

The practical test loop is short. Read status on the target device to learn its real tier. Use manual to pin your process at the background cap for that tier, drive the app through its heaviest path with the UI hidden, and watch for the MemoryLimiter:AnonSwap exit. That reproduces on a 12 GB developer handset what a user on a 4 GB device will hit in production.

What actually changes in the build

The remediation list Google publishes has not changed since June, and none of it is new engineering. Enable full R8: set isShrinkResources = true and isMinifyEnabled = true, switch from proguard-android.txt to proguard-android-optimize.txt (the legacy file blocks optimisation and is no longer supported in Android Gradle Plugin 9), and remove android.enableR8.fullMode = false from gradle.properties. Narrow the keep rules; global -dontoptimize, -dontshrink and -dontobfuscate switches turn the whole thing off. Google's published example is Monzo, which enabled full R8 optimisation and recorded a 35% reduction in ANR rate, a 30% improvement in cold start rate and a 9% reduction in app size.

Bitmaps are the next lever, because a 100 KB compressed file decodes into several megabytes of resident pixels. Downsample rather than loading full-resolution images into small views, use RGB_565 instead of the default ARGB_8888 wherever transparency is not needed (half the memory), prefer vector drawables for geometric assets, and return bitmaps to the pool in Coil or Glide instead of allocating fresh ones.

Then implement onTrimMemory in your Application class through `ComponentCallbacks2`, and handle only TRIM_MEMORY_UI_HIDDEN and TRIM_MEMORY_BACKGROUND. Since Android 14 the system has stopped delivering the other legacy constants, and they were formally deprecated in Android 15. Code that still branches on TRIM_MEMORY_MODERATE or TRIM_MEMORY_RUNNING_LOW is dead code. That matters more under Android 17 than it did before, because TRIM_MEMORY_UI_HIDDEN is now the moment your process moves into the smaller cap.

Teams already working through the wider Android 17 API 37 behavior changes should treat memory as a separate workstream from the targetSdk bump. Memory limits apply to all apps on Android 17 regardless of the level you target, so they land before you touch targetSdk 37 and the adaptive layout work.

India-specific considerations

The tier table is a pricing question in India, not just an engineering one. IDC reported India's smartphone shipments down 4.1% year over year to 31.0 million units in Q1 2026, with average selling price at a record US$302, up 10.4%. The sub-US$100 band fell 59% and its share halved from 18% to 8%, while the US$100 to US$200 band grew 10% and rose from 39% to 45% share.

Aditya Rampal, senior research analyst, Devices Research, IDC Asia Pacific, attributed the move to component costs: "Average selling prices increased 10.4% YoY to a record US$302 in Q1 2026, driven by persistent memory cost inflation across both newly launched devices and existing models."

Memory inflation pushes new budget models toward less RAM, not more. For an Indian consumer app that means a large and growing share of sessions on the 4 GB and 6 GB limit sets, where the background cap is 1024 MiB or 2048 MiB. A design that assumes an 8 GB floor is designing for the shrinking half of the market. Under the DPDP Act 2023, any local caching you add to reduce memory churn still has to respect purpose limitation and retention, so cache aggressively for performance and clear it on the same schedule you already publish.

What is still unknown

Three things are not yet documented. Google has not published a date for the in-field memory metrics it says are coming to Play Console. There is no published policy tying memory-limit terminations to Play technical-quality thresholds the way ANR and crash rates are tied today, so the enforcement is a platform behaviour and not yet a store one. And because the vendor override exists, there is no way to enumerate the real caps across the Android fleet from the AOSP defaults alone; a device-level survey is the only honest answer. The real cost here is usually the measurement, not the fix.

FAQ

How eCorpIT can help

eCorpIT is a CMMI Level 5 and ISO 27001:2022 certified engineering organisation in Gurugram, and our Android teams run memory work as a measured exercise rather than a guess: read the real tier off the device, pin the process with am memory-limiter manual, and reproduce the failure before changing code. That is the same instrumentation loop behind our Android vitals ANR and crash remediation work, and it fits inside the broader enterprise mobile app development programmes we run. If your app leans on a foreground service and ships to 4 GB and 6 GB devices, talk to our senior engineering team at /contact-us/.

References

  1. Memory Limiter, Android Open Source Project - mechanism, process-state table, XML schema and the standard platform limit sets.
  1. Prioritizing Memory Efficiency: Essential Steps for Android 17, Android Developers Blog, 2 June 2026 - Google's enforcement framing, detection recipe and remediation list.
  1. Manage your app's memory, Android Developers - the memory guidance Google points to for baselines and diagnosis.
  1. ApplicationExitInfo, Android API reference - getDescription() and the REASON_OTHER exit reason.
  1. ComponentCallbacks2, Android API reference - onTrimMemory() and the surviving trim levels.
  1. Android Debug Bridge (adb), Android Developers - the shell used to run the am memory-limiter subcommands.
  1. Low memory killer daemon, Android Open Source Project - the LMK behaviour the Memory Limiter is designed to relieve.
  1. Cgroup abstraction layer, Android Open Source Project - the cgroup v2 layer the Memory Limiter builds on.
  1. Keep rules best practices, Android Developers - narrowing R8 keep rules to recover optimisation.
  1. Coil image loading library - Google's recommended loader for Kotlin and Jetpack Compose projects.
  1. Glide image loading library - Google's recommended loader for Java-based applications.
  1. Heap Dump Explorer, Perfetto documentation - the default heap-dump view for profiles captured by ProfilingManager triggers.
  1. Why India's Smartphone Market Declined 4.1% in Q1 2026, IDC, 12 May 2026 - shipment, ASP and price-band figures, and the Aditya Rampal quote.

Last updated 20 August 2026.

Frequently asked

Quick answers.

01 What is the Android 17 memory limit on a 6 GB phone?
AOSP's default configuration gives a 6 GB tier device a memory.high cap of 4096 MiB for visible processes and 2048 MiB for processes that are not visible, with swap capped at 2048 MiB for both. The tier is selected by MemTotal, which must be at least 4608 MiB.
02 Does Android 17 kill my app when it exceeds the memory limit?
Not immediately. AOSP describes memory.high as a soft limit: the kernel throttles the process, evicts clean file-backed pages and swaps out inactive anonymous memory while the app keeps running. Termination happens in the extreme case where the app keeps allocating anonymous memory after swap is exhausted.
03 Do foreground services get the larger memory limit?
No. AOSP's process-state table classifies FOREGROUND_SERVICE and BOUND_FOREGROUND_SERVICE as "Not visible", so they receive the restrictive cap. On a 6 GB device that is 2048 MiB rather than 4096 MiB. Only TOP, BOUND_TOP, TOP_SLEEPING and IMPORTANT_FOREGROUND are treated as visible.
04 How do I detect a memory-limit termination in production?
Call getDescription() on ApplicationExitInfo. A session ended by the Memory Limiter reports exit reason REASON_OTHER with a description string containing MemoryLimiter:AnonSwap. Without parsing that string the event looks like an ordinary REASON_OTHER exit, which is why it is easy to miss in a crash dashboard.
05 Can I test the memory limits on a device with plenty of RAM?
Yes. Android 17 adds three adb am subcommands: am memory-limiter status reports the current caps, am memory-limiter manual <pid> <limit> pins one process to a limit in megabytes, and am memory-limiter ignore exempts a UID or every app. Pin your process at a lower tier's cap to reproduce it.
06 Does the memory limit apply if my app targets an older API level?
Yes. The memory limits apply to all apps running on Android 17, regardless of the targetSdkVersion the app declares. That places them in the same category as other Android 17 changes for all apps, and separates the memory work from the schedule for bumping your target API level to 37.
07 Which onTrimMemory levels still fire on Android 17?
Only TRIM_MEMORY_UI_HIDDEN and TRIM_MEMORY_BACKGROUND. Google states that since Android 14 the system stopped delivering the other legacy constants, which were formally deprecated in Android 15. Handling code branched on the older levels no longer runs, so it should be consolidated onto the two surviving signals.
08 Why does the 16 GB tier require only 13312 MiB of MemTotal?
Because MemTotal reports the DRAM left after hardware carve-outs for the GPU, baseband modem, camera ISP, secure execution environments and hypervisors, which AOSP says typically consume between 500 MiB and over 1 GiB. The thresholds include that margin so devices land in their intended tier despite board-level variation.

About the author

Manu Shukla

Founder & Director

Founder of eCorpIT. Hands-on engineer leading senior-only delivery for AI apps, custom software, and cloud systems for global clients.

Subscribe

One engineering note a week. No fluff, no spam.

Senior-architect playbooks on AI agents, mobile apps, cloud, security, data, and marketing — delivered every Wednesday.

Past the reading

Read enough. Let's build something.

A senior architect responds in 24 working hours with scope, indicative cost, and a timeline. NDA before any technical conversation.