Scroll jank appears only on older devices and only after 5 minutes — reproduce, measure, root-cause it
Users report scroll jank that appears only on older devices and only after ~5 minutes of use. On your latest device everything is smooth. Below are two captures on an older device: a fresh launch and after five minutes of browsing.
Fresh After 5 min
Frame rate (scroll) 60 fps 38 fps
Resident memory 190 MB 540 MB
Main-thread self 8 ms/frame 22 ms/frame
Top self symbol render ImageCache.subscript
Explain how you reproduce, measure, and root-cause it.
Reproduce on the old device and drive it five minutes — the accumulated state, not a cold launch, matters. Measure with Time Profiler and Allocations together. Memory climbs to 540 MB with ImageCache on top — an unbounded cache causing pressure. Bound the cache.
- ✗Profiling a fresh launch on a new device instead of the aged state on an old one
- ✗Dismissing time-dependent jank as not reproducible
- ✗Ignoring memory growth as a cause of scroll jank
- →Why does memory pressure show up as scroll jank rather than a crash first?
- →How would you confirm the image cache is the growth, not a leak?
The bug is time- and device-dependent, so the setup is the whole game: profile on the oldest supported device (its smaller memory and slower CPU are the trigger) and drive it for five minutes to reach the janky state — a cold launch never reproduces it. Run Time Profiler and Allocations together so you correlate frame cost with memory over the session:
Fresh After 5 min delta
Resident memory 190 MB 540 MB +350 MB, monotonic
Main self/frame 8 ms 22 ms work per frame nearly tripled
Top self symbol render ImageCache the cache now dominates
The signature — memory climbing monotonically and per-frame work rising with it, ImageCache on top — is an unbounded cache. As it grows the device hits memory pressure, the allocator and decode paths do more work per frame, and scrolling drops frames. Confirm with Allocations generational marks (growth is retained, not leaked), then bound the cache with an NSCache count/cost limit or eviction. Smoothness returns and memory plateaus.