KHD
KHD Labs Mobile & Web Studio

Memory Jetsam on iOS: Why Your App Silently Dies in the Background on Older iPhones

Why Sentry and Crashlytics show zero stack traces when iOS kills your app, how dirty memory pages trigger kernel Jetsam eviction, and how to measure real memory footprint.

Elena V.
•
Principal Systems Architect
• 10 min read
Studio Benchmark
Jetsam background kill rate: 19.4% down to 0.1%
Problem Addressed: "Users complain the app constantly restarts when switching between apps"

A health-tech client came to us with a mystery that was tanking their App Store rating.

Users with iPhone 11 and iPhone 12 devices were furious. They would open the app, start filling out a 10-step medical intake form, switch to Apple Wallet or their camera to snap an insurance card, switch back—and the app would restart from scratch. All form data lost.

The engineering team looked at Sentry and Firebase Crashlytics. Zero crashes reported. Their crash-free session metric proudly displayed 99.9%.

“It must be an iOS bug,” they told the product manager.

It wasn’t an iOS bug. It was Jetsam.

How iOS Silently Murders Your App

iOS doesn’t have a disk swap file like macOS or Windows. When physical RAM runs low, the Darwin kernel has two options: compress memory pages, or terminate processes.

The kernel subsystem responsible for this is called Jetsam (memorystatus). It maintains a strict priority queue of running processes:

[Foreground App] ── High Priority
      │
[Audio / GPS Background Tasks] ── Medium Priority
      │
[Suspended Background Apps] ── Lowest Priority (First to be killed)

When your user switches away from your app to open the camera, the iOS Camera app requires up to 700MB of RAM to run real-time neural network filters and RAW image processing.

The kernel scans suspended apps. It looks at Dirty Memory—memory that has been written to and cannot be purged from disk. If your app is holding onto 280MB of dirty memory because of an uncompressed bitmap cache or un-garbage-collected JavaScript context, the kernel issues a SIGKILL (kill -9).

No exception is thrown. No signal handler gets invoked. Your crash reporting SDK never gets a chance to write a crash log. To Crashlytics, your app simply closed normally.

To the user, your app just crashed and erased their intake form.

Diagnosing Jetsam with Xcode and IPS Logs

You will never catch Jetsam issues in the iOS Simulator on an Apple Silicon Mac with 64GB of unified memory. You must test on a physical low-memory device (an iPhone SE 2nd Gen or iPhone 11 is our studio gold standard).

Connect the device to a Mac and navigate to: Settings > Privacy & Security > Analytics & Improvements > Analytics Data

Search for logs starting with JetsamEvent-.... Open one:

{
  "timestamp": "2026-01-28 14:12:05.10 -0500",
  "bug_type": "298",
  "os_version": "iPhone OS 18.2",
  "incident_id": "89A42C18-...",
  "pageSize": 16384,
  "states": ["frontmost", "background"],
  "processes": [
    {
      "uuid": "4391A...",
      "procname": "ClientMobileApp",
      "reason": "per-process-limit",
      "rpages": 19482, // 19,482 pages * 16KB = ~311 MB dirty footprint!
      "jetsam_level": 0
    }
  ]
}

There is your proof: reason: per-process-limit. The kernel executed your process because it exceeded its allocated memory ceiling while backgrounded.

The Tri-Fold Remedy

  1. Evict Image Caches on Backgrounding: In your native AppDelegate or React Native AppState listener, flush all in-memory image decoders the moment the app enters the background:
func applicationDidEnterBackground(_ application: UIApplication) {
    // Drop all decoded in-memory CGImage buffers
    ImagePipeline.shared.configuration.imageCache?.removeAll()
    URLCache.shared.removeAllCachedResponses()
}
  1. Never Hold High-Res Bitmaps in Views: If an avatar displays at 48x48 points on a 3x Retina screen, it needs a 144x144 pixel buffer (~82KB). If you load the user’s raw 12-megapixel photo directly into an UIImageView, iOS allocates a 48MB uncompressed bitmap buffer in memory.
  2. Persist Form State Incrementally: Never rely on RAM to keep state across app switches. Write every keystroke into an encrypted SQLite or MMKV scratch buffer so that when the app restarts after a Jetsam event, it resumes on the exact same field seamlessly.

Fixing this one issue restored our client’s App Store rating from 2.9 to 4.7 in eight weeks.

Engineering Consultation

Encountering this bottleneck in your application?

We profile production repositories directly with native tooling (Xcode Instruments, Android Studio Profiler) to pinpoint the exact root causes.