June 10, 2026

PBX Science

VoIP & PBX, Networking, DIY, Computers.

New Memory Limits in Android 17 Can Kill Your App Without a Crash Log



Android 17 Memory Limits: What Developers Need to Know
ANDROID 17 Developer Bulletin June 2026
Breaking Change

New Memory Limits in Android 17 Can Kill Your App Without a Crash Log

⚠ Behavior Change Subset of Devices Only No Stack Trace

Starting with Android 17, Google has introduced per-process memory limits based on a device’s total RAM. When an app exceeds its limit, the system can terminate the process immediately — without generating a standard crash stack trace. While most well-optimized apps will see no change, the new policy targets memory leaks, oversized image caches, and runaway foreground services before they destabilize the whole device.

ℹ️

Important: Memory limits are only enforced on a subset of Android 17 devices. However, every Android developer should prepare now — the enforcement footprint will expand over time.

How Are Restrictions Triggered?

Previously, Android’s Low Memory Killer (LMK) handled memory pressure by quietly pruning background processes first. If a single app consumed excessive memory, the system would reclaim memory from other cached apps, turning those apps’ next launch into a cold start with potential state loss.

Android 17 takes a more decisive approach: limits are set per process based on the device’s total RAM. Once a process exceeds that ceiling, the system terminates it directly. This prevents a misbehaving process from dragging down the entire multitasking experience.

Crucially, this is not a Java heap OutOfMemoryError — no clean stack trace appears in your crash platform. When a user reports “the app was killed by the system” with no associated crash, the new memory limiter is a prime suspect.

Detecting the Exit: ApplicationExitInfo

Since Android 11, ActivityManager.getHistoricalProcessExitReasons() lets you read why a process was killed. For memory-limit exits, the identifying fingerprint is a combination of two fields: the exit reason is REASON_OTHER and the description contains the string "MemoryLimiter:AnonSwap". Checking REASON_OTHER alone is insufficient — that code covers many other exit scenarios.

Kotlin
fun findMemoryLimiterExit(context: Context): Boolean {
    val activityManager = context.getSystemService(ActivityManager::class.java)
    val exits = activityManager.getHistoricalProcessExitReasons(
        context.packageName,
        0,
        20
    )
    return exits.any { info ->
        info.reason == ApplicationExitInfo.REASON_OTHER &&
        info.description?.contains("MemoryLimiter:AnonSwap") == true
    }
}

Local Reproduction with am memory-limiter

Android 17’s behavior change documentation introduces the am memory-limiter adb command, which lets you inspect and manually override memory limits for any running process — an essential tool for reproducing issues locally before they surface in production.

Prerequisites

These commands only work on devices that have the memory limiter feature enabled. On devices without the limiter, commands will have no effect.

# 1. Get the PID for your app
adb shell pidof com.example.app

# 2. Check current memory limiter status
adb shell am memory-limiter status

# 3. Set a low limit (e.g. 300 MB) to trigger enforcement
adb shell am memory-limiter manual <pid> 300

# 4. Restore system defaults
adb shell am memory-limiter manual <pid> none

# 5. Remove all limits for the process
adb shell am memory-limiter manual <pid> max

# 6. Ignore specific or all UIDs
adb shell am memory-limiter ignore <uid>
adb shell am memory-limiter ignore all
adb shell am memory-limiter ignore none

Reduce Memory Footprint: Enable R8 Fully

Memory optimization should go beyond heap dumps. The code size, dead resources, and overly broad reflection-keep rules in your release APK all contribute to runtime resident memory. At a minimum, confirm that R8 shrinking and optimization are not accidentally disabled.

build.gradle.kts
android {
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}
⚠ Do Not Use

Avoid proguard-android.txt — it favours older compatibility behaviors and blocks key optimizations. It is also no longer supported in AGP 9. Use proguard-android-optimize.txt instead.

In gradle.properties, remove any line that disables R8 full mode:

# Delete this line from gradle.properties if it exists:
android.enableR8.fullMode=false

In proguard-rules.pro, avoid blanket switches such as -dontoptimize, -dontshrink, or -dontobfuscate. Scope keep rules to specific classes, fields, or annotations rather than entire packages.

Images & Memory Leaks

Images are one of the most underestimated sources of Android memory pressure. A PNG compressed to a few hundred kilobytes expands dramatically when decoded into ARGB_8888 — memory usage scales with pixel dimensions, not file size. Use Coil in Compose projects and Glide in View-based projects; never bypass these libraries with custom large-image loading. Always size decode output to match the display size of the target View or Composable.

For images without transparency requirements (profile photo thumbnails, list items), evaluate RGB_565, which uses half the per-pixel memory of ARGB_8888.

Duplicate bitmaps can be spotted directly in Android Studio Profiler’s Heap Dump view — it flags duplicates and shows image previews inline, making it straightforward to trace incorrect caching strategies.

Android Studio Panda 3 has added a dedicated LeakCanary profiler task that offloads memory leak analysis from the device to the development machine. Leak traces are contextualized with source code links, dramatically reducing time-to-fix for issues like uncleared Fragment bindings, unregistered listeners, and unreleased Compose DisposableEffects.

Proactively Release Cache with onTrimMemory()

When an app moves to the background, the system may reclaim memory. Because the system doesn’t know which objects are cheap to rebuild, implement onTrimMemory() in your Application class to guide that process manually.

Kotlin
class App : Application(), ComponentCallbacks2 {

    override fun onTrimMemory(level: Int) {
        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            imageMemoryCache.clear()
            videoPreviewCache.clear()
        }
        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            searchResultCache.clear()
            temporaryBufferPool.trim()
        }
    }
}

Note that in Android 14 and later, some older TRIM_* constants were deprecated and are no longer issued. Do not release non-recoverable business state here — drafts in progress, payment flow status, and user navigation choices should be handled through ViewModel/saved state, not cleared as cache.

Online Monitoring with ProfilingManager

Some memory issues only surface in production. Android 15 introduced ProfilingManager for in-app profiling callbacks; Android 17 expanded this with two new trigger types especially relevant to the memory limiter:

TRIGGER_TYPE_OOM — fires on the next app launch after an OutOfMemoryError crash, collecting a Java heap dump for upload.
TRIGGER_TYPE_ANOMALY — fires when the system detects a severe performance anomaly, including when the Android 17 memory limit is about to be breached. It triggers before the process is killed, giving you a heap dump at exactly the right moment.

Kotlin
val profilingManager = context.getSystemService(ProfilingManager::class.java)
val executor = Executors.newSingleThreadExecutor()

profilingManager.registerForAllProfilingResults(executor) { result ->
    if (result.errorCode == ProfilingResult.ERROR_NONE) {
        enqueueProfileUpload(result.resultFilePath)
    } else {
        logProfilingError(result.errorCode)
    }
}
Privacy Notice

Heap dumps may contain in-memory object content. Consider sampling ratios, user consent, upload timing, file size limits, and retention policies before deploying to production. These are not ordinary logs.

Summary: Action Checklist

The most important items for Android 17 memory limit readiness:

✅ Developer Checklist

  • Detect memory-limit exits via REASON_OTHER + "MemoryLimiter:AnonSwap" in ApplicationExitInfo
  • Reproduce locally with adb shell am memory-limiter manual <pid> <mb>
  • Confirm isMinifyEnabled = true and proguard-android-optimize.txt in release builds
  • Remove android.enableR8.fullMode=false from gradle.properties
  • Scope keep rules to specific classes — avoid -dontoptimize and blanket -keep rules
  • Implement onTrimMemory() to release UI caches and preview buffers when backgrounded
  • Use Android Studio Panda’s LeakCanary profiler task to catch leaks in development
  • Register TRIGGER_TYPE_ANOMALY with ProfilingManager for production heap dump capture

New Memory Limits in Android 17 Can Kill Your App Without a Crash Log


Windows Software Alternatives in Linux


Disclaimer of pbxscience.com

PBXscience.com © All Copyrights Reserved. | Newsphere by AF themes.