← All notes
Device Testing6 min

A memory kill is not a crash

On Endeo betas we still get tickets that say the app closed itself mid-page. Crash-free looks fine. The Organizer crash list is quiet. What actually happened is often jetsam: iOS ended the process for exceeding a memory limit. That is a system kill, not an exception your crash reporter caught. iOS 27 finally ships MetricKit memory exception diagnostics as their own case. Treating every "app disappeared" report as a missing crash stack is how memory kills stay invisible while you hunt for a .ips that was never filed.

Three ways a process ends, three questions

A crash diagnostic answers how the process failed in your code or a library: exception type, termination reason, call stack. Crash-free session percentage tracks that family of endings. It is the queue most teams already staff.

A hang diagnostic answers where the main thread stuck while the process was still alive. The user may force-quit later. The investigation packet is still a hang report, not a crash — the same split we already keep when TestFlight screenshots land next to crash feedback.

A memory kill answers whether the system terminated you for memory pressure. Until recently that chapter was thin: support said "closed by itself," your crash sink shrugged, and Allocations work started only if someone reproduced the cliff on a tethered device. Memory exception diagnostics close that gap with an event-based report you can switch on the same way you switch crash and hang.

Why crash queues miss the kill

Crash reporters and Organizer crash lists are built around exception and signal paths. Jetsam is the kernel reclaiming memory. There is often no crash feedback item, no dSYM hunt for a crash signature, and no bump to the abnormal-exit story your dashboard already watches — unless you also ingest memory exceptions and termination categories that name how the exit was counted.

Folding memory kills into the crash column creates the same category error as labeling a screenshot a crash. Engineers open Organizer looking for a stack that belongs to a different diagnostic type. Product averages "crashes" that never entered the crash pipe. The real signal — which surface retained full-resolution ink, Vision buffers, or thumbnail caches — never gets an owner.

Crash-free rate as a ship gate already fails when hangs dominate. Memory kills are the quieter cousin: users experience them as crashes, metrics may not, and the triage path is Allocations and VM Tracker once you have a field event that says memory, not crash.

How we triage each packet

Crash diagnostics stay in the crash queue: download or Organizer link, symbolicate, group by signature, then read tester comments as context. Hang diagnostics stay on the hang path: duration, stack, sustained-session reproduce — not a crash label because someone said the UI froze.

Memory exception diagnostics land as memory first. Confirm the MetricKit case (or the Organizer equivalent), keep the call stack tree, match dSYMs for that binary, and open a memory investigation — which editor path, which extension, which notebook size — instead of filing "cannot reproduce crash." Extensions matter here: widget and other extension memory limits are easy to blow past while the host app looks fine in a short desk session.

Automation should preserve the split the same way crash and hang already should. A single GitHub label named "stability" that imports every MetricKit diagnostic recreates one queue. Separate labels, separate owners, same-day disposition. Encode and durably enqueue the raw report before you classify it — unknown cases stay recoverable when Apple adds more diagnostic kinds.

What we check before calling field stability "done"

Crash, hang, and memory exception streams each have a labeled sink on TestFlight builds — not only crash. At least one real memory-heavy path has been exercised on a non-debug install (large notebook, dense ink, OCR coalescing, extension refresh) so the channel is proven the same way we prove hang delivery.

No support "app closed itself" ticket is closed as UX-only without checking whether a memory exception or jetsam log exists for that build and device window. No memory exception is retitled as a crash for dashboard vanity. Short cold starts still do not count as proof; memory cliffs show up on sustained sessions.

If those checks fail, stability theater continues: green crash-free, quiet crash inbox, and users who keep losing work when iOS reclaims the process. The useful discipline is simple — treat memory kills as their own diagnostic family, give them a stack and an owner, and stop asking the crash queue to catch what it was never built to see.

Questions

Is a memory kill the same as a crash for crash-free metrics?
No. Crash-free tracks crash-style terminations. Memory-pressure kills are a different ending. Ingest memory exception diagnostics (and termination categories that tie exits to metrics) instead of assuming every user-visible "crash" appears in the crash rate.
Where do we start when someone says the app closed itself?
Check crash feedback and Organizer crashes first, then hang diagnostics if the UI froze, then memory exception / jetsam evidence for that build. Do not burn a day on a missing .ips before you ask whether the process was killed for memory.
Does MetricKit deliver memory exceptions from an Xcode launch?
No. MetricKit field delivery still arms on TestFlight and App Store installs. Desk memory work uses Instruments Allocations and friends; proving the memory-exception channel needs a distributed build the same way hang delivery does.

Sources

  1. WWDC26 — Meet the new MetricKit — Memory exception diagnostics; crash vs hang diagnostic cases on MetricManager.
  2. Apple — Monitoring app performance with MetricKit — DiagnosticResult cases including memoryException; event-based diagnosticReports.
  3. Related: A crash-free rate is not a ship gate
  4. Related: An Xcode launch is not a MetricKit session

Have something to build?

Tell us what you're working on and we'll tell you honestly whether we're the right fit.

Work with us