Memory grows but the Leaks tool finds nothing — what is an abandoned object graph?
While browsing, memory grows and never comes back down, yet the Leaks instrument reports zero leaks. Using the Allocations instrument you mark a generation after each screen visit.
Explain what an abandoned object graph is, why Leaks misses it, and which tool and technique find it. Below are the generation markers.
Generation Growth Persistent Category (top)
Gen A +12.4 MB 12.4 MB ImageBuffer, DetailViewModel
Gen B +12.1 MB 24.5 MB ImageBuffer, DetailViewModel
Gen C +12.3 MB 36.8 MB ImageBuffer, DetailViewModel
Leaks: 0 leaked blocks
Diagnose the cause.
An abandoned object graph is reachable memory, held by a strong reference, that is never used again, so Leaks — which reports only unreachable blocks — misses it. Allocations generational analysis shows each screen leaving ~12 MB persistent, the retained graph.
- ✗Assuming any memory growth without a leak is fragmentation and unfixable
- ✗Believing the Leaks instrument catches every kind of memory growth
- ✗Confusing an abandoned graph with a retain cycle or an autorelease-pool issue
- →Why does the Leaks instrument report zero while memory is still climbing?
- →How does marking a generation per screen isolate the retained objects?
An abandoned object graph is memory that is still reachable — some strong reference keeps pointing at it — but that the app will never use again. The Leaks instrument only reports blocks that have become unreachable with a nonzero retain count, so a reachable-but-dead graph is invisible to it. That is why Leaks says zero while memory climbs:
Gen A +12.4 MB → 12.4 MB persistent
Gen B +12.1 MB → 24.5 MB persistent each screen visit leaves ~12 MB that never frees
Gen C +12.3 MB → 36.8 MB persistent same categories: ImageBuffer, DetailViewModel
The tool is the Allocations instrument with generational analysis: mark a generation on entering the screen and another after leaving. Objects that survive to the next generation and keep accumulating are the abandoned graph. Here DetailViewModel and its ImageBuffer persist across every generation — a cache that never evicts, or a closure/observer holding self. Break the strong reference (evict the cache, capture [weak self]) and the generations stop growing.