Strategies for minimizing OOM crashes?

Hello,

We are facing many memory related crashes. Right now this is our top crash reason and after reading through many other useful posts in the forums, such as "how Xcode to calculate Memory" where @eskimo provided lots of useful info, and talks such as iOS Memory Deep Dive I figured out it was time to post a new thread to see if anyone can help.

Our crashes generally happen when drawing a PDF (using PDFKit's draw(with:to:)), although they also happen in other places where memory is needed such as Array.insert (which ends up calling swift_slowAlloc and swift_slowAlloc.cold), or std::bad_alloc.

The exceptions are either EXC_BREAKPOINT on CFRetain, NSMallocException (Failed to grow buffer), or the previously mentioned std:bad_alloc.

In addition to this, we have many other OOM that are not tracked by our crash reporting tool.

For crashes that are reported, the average free RAM our users have ranges from 30 MB to 120 MB. In other cases the user seems to have 400 MB of free RAM or even more.

I have a few questions around this that I hope are also useful for other folks.

  1. Besides general recommendations of using less memory in general, what's the recommended approach for minimizing these memory issues?

Most of our disposable memory is in held in NSCaches (with reasonable total cost limits and properly calculated cost per object). On this case, is it recommended to handle the NSMallocExceptions, try to free memory, and retry the operation? As mentioned, in many cases we don't even get memory warnings prior to the crashes.

I'm wondering if memory fragmentation is also affecting us, but I don't think there's much we can do about this.

I noticed PDFKit requesting allocations as large as 30 MB to render a 100x100 thumbnail (when I simulated a situation where I ran out of VM, I caught that with the ASAN, which reports the memory that was attempted to be allocated).

Another alternative I could think of is to try to use os_proc_available_memory() and try to free some memory if memory is low. The reasoning behind this is that since we have no way to know how much memory the operation it's going to use, but we have data of free memory of users with crashes, we could try to free up memory to be above that. I'm not sure how effective would that be though.

  1. As some of these users seem to have enough RAM, they might be running out of virtual memory. What's the best way to track Virtual Memory, to use it as a hint in our crash reports? Reading the virtual_size from task_vm_info in my iPad returns over 400 GB. I was expecting this to be the virtual used size, like the size that gets reported on the VM Tracker instrument. I mention this because I was able to reproduce these crashes by memory mapping ~50 GB of a 200 MB file in a 2017 iPad Pro, and then doing other unrelated memory operations.

  2. For apps that are document based, with arbitrary-sized documents, is it recommended to ask for the extended virtual addressing entitlement and the increased memory limit one? Could these help on lower end devices?

  3. Would listening to memory pressure events help at all? There's a .warning and .critical memory pressure events. I'm wondering wether the system wide warnings only are sent when the memory pressure is critical, although it doesn't look like it looking at the names of the constants.

  4. If I'm using NSCache, is it safe (memory compression wise) to empty the cache under some circumstances? Put it in a different way: does emptying an NSCache causes the memory compressor to decompress the data?

  5. Are there any other recommendations or things that I might have missed?

Thanks a lot in advance!

Strategies for minimizing OOM crashes?
 
 
Q