Mobile Development 14 min read

Large Memory Allocation Monitoring Solution for iOS Applications

A low‑overhead monitoring solution for iOS apps resets the global malloc_logger to capture every allocation, filters large allocations exceeding a configurable threshold, records lightweight stack traces using dyld image maps, and sends address‑only data for offline symbolication, enabling developers to identify and fix memory‑heavy code, reducing OOM crashes.

Baidu App Technology
Baidu App Technology
Baidu App Technology
Large Memory Allocation Monitoring Solution for iOS Applications

Background: iOS applications often encounter Out‑Of‑Memory (OOM) crashes because the system cannot capture OOM exceptions or provide stack traces. When memory usage reaches a dangerous level, the Jetsam mechanism may terminate the process, especially when a single allocation exceeds a threshold (e.g., >30 MB in Baidu App).

To address this risk, a large‑memory‑allocation monitoring scheme was designed that leverages both online monitoring and offline pipelines, providing richer stack information while keeping performance impact minimal.

Technical Overview : The solution consists of two essential modules – (1) obtaining detailed memory‑allocation information and (2) collecting stack traces for large allocations.

1. Getting Memory Allocation Details : By resetting the global malloc_logger pointer in libsystem_malloc.dylib , every call to the allocation APIs ( malloc_zone_malloc , malloc_zone_calloc , malloc_zone_valloc , malloc_zone_realloc , malloc_zone_free ) triggers a logger callback that reports the allocation size, zone address and result pointer. This works for both Objective‑C objects and C/C++ objects without using fishhook , thus avoiding any modification of the Mach‑O binary.

void *malloc_zone_malloc(malloc_zone_t *zone, size_t size) { MALLOC_TRACE(TRACE_malloc | DBG_FUNC_START, (uintptr_t)zone, size, 0, 0); void *ptr = zone->malloc(zone, size); if (malloc_logger) { malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE, (uintptr_t)zone, (uintptr_t)size, 0, (uintptr_t)ptr, 0); } MALLOC_TRACE(TRACE_malloc | DBG_FUNC_END, (uintptr_t)zone, size, (uintptr_t)ptr, 0); return ptr; }

The logger’s type field distinguishes allocation from deallocation, enabling the solution to filter out free events. A table of API‑type mappings (e.g., MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE for malloc_zone_malloc ) is used to isolate true allocation events.

2. Resetting malloc_logger : The following steps are performed at app start‑up:

Import <malloc/malloc.h> .

Define a logger function with the exact signature:

typedef void (malloc_logger_t)(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t num_hot_frames_to_skip); extern malloc_logger_t *malloc_logger;

Save the original logger pointer:

malloc_logger_t *origin_malloc_logger = malloc_logger;

Replace it with a custom implementation:

malloc_logger = (malloc_logger_t *)bba_malloc_stack_logger; void bba_malloc_stack_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t backtrace_to_skip) { if (origin_malloc_logger) { origin_malloc_logger(type, arg1, arg2, arg3, result, backtrace_to_skip); } // large‑allocation detection and stack capture ... }

When a single allocation exceeds the configurable threshold (default 8 MB, adjustable from the server), the custom logger proceeds to collect a stack trace.

3. Stack Trace Collection : The solution first builds a complete map of all loaded Mach‑O images using dyld APIs ( _dyld_image_count , dyld_get_image_name , dyld_get_image_header , _dyld_get_image_vmaddr_slide ) on a background thread, so no main‑thread resources are consumed.

For a qualifying allocation, backtrace is called on the allocating thread (depth = 20 for production, 40 for offline pipelines):

size_t depth = backtrace((void **)stacks, 20);

The captured addresses are then matched against the dyld image map to obtain the library name, base address and offset, forming a crash‑style line such as:

libsystem_kernel.dylib 0x1b8a9dcf8 0x1b8a73000 + 175352

This lightweight address‑only format is sent to the server, where atos together with the app’s dSYM files resolves the human‑readable symbols, eliminating the heavy symbolication cost on the device.

Results : The monitoring is deployed both in production and in the offline CI pipeline. It achieves three goals:

Reduces OOM rate by exposing and fixing unreasonable large allocations.

Provides data‑driven insight into which app scenarios trigger large allocations.

Prevents future OOMs by giving developers a clear, low‑overhead monitoring feedback loop.

performancestack traceiOSmallocoommemory-monitoring
Baidu App Technology
Written by

Baidu App Technology

Official Baidu App Tech Account

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.