AOSP Framework & Internals
5 min read

ION Memory Allocator

Understand how Android historically managed specialized memory allocations for complex hardware like cameras and GPUs.

Why Software Memory Fails Hardware

Software processes are remarkably forgiving about memory. They rely on the Memory Management Unit to make scattered pages of physical RAM look like one contiguous block. You can ask the operating system for a chunk of memory, and it will piece together fragments from anywhere it finds them.

Hardware chips do not share this luxury. Many hardware components in a smartphone interact directly with physical memory without an MMU. Some Graphics Processing Units require memory blocks that are physically contiguous in the actual RAM chips. Camera Image Signal Processors often demand memory from highly secure, isolated physical zones.

If you pass a standard scattered memory buffer to a hardware chip that expects contiguous physical memory, the system will crash. Historically, every hardware vendor solved this by writing their custom memory allocator inside the Linux kernel. This approach created massive fragmentation across the Android ecosystem. Android needed a unified way to allocate memory for picky hardware components.

Centralizing Hardware Memory with ION

The proliferation of custom memory allocators made it impossible to share memory between different chips. If the display driver and the camera driver each managed their own pools of RAM, they could not easily exchange data. Google introduced the ION Memory Allocator to solve this driver fragmentation.

ION acts as the single authority for hardware memory requests across the entire system. Instead of individual drivers managing their own memory, they all delegate that responsibility to ION. When a hardware component needs memory with specific physical characteristics, it asks this central authority.

ION handles these wildly different physical memory requirements by categorizing physical RAM into distinct pools called Heaps. When you request memory from ION, you must specify which heap you need.

Each heap maps to specific hardware traits. The System Heap provides standard, non-contiguous memory for drivers that have an IOMMU to handle scattered pages. The CMA Heap guarantees physically contiguous memory for simple hardware blocks that lack address translation. The Carveout Heap reserves secure, isolated chunks of RAM during boot to prevent the main CPU from reading sensitive data like DRM video.

By explicitly selecting a heap, the driver ensures the hardware receives memory it can actually process. But allocating specialized memory is only half the problem. You also need a way to pass that memory between different hardware chips efficiently.

The Zero-Copy Hardware Pipeline

Copying a 20MB uncompressed video frame from the camera to the encoder 60 times a second would overwhelm a mobile CPU instantly. The data needs to flow between physical chips without the main processor touching it. ION solves this by using File Descriptors to represent physical buffers.

When you allocate memory from an ION heap, the kernel returns a standard Linux file descriptor. This descriptor acts as a universal ticket for that specific physical memory block. You can pass this ticket around the system freely.

The diagram below shows how the Camera application orchestrates a zero-copy pipeline using an ION file descriptor. This visualization helps clarify how data moves directly between hardware components. Watch how the file descriptor is passed around, but the pixel data goes straight from the sensor to the encoder.

The Camera app asks ION for a memory buffer and receives a file descriptor. It passes this descriptor to the Camera HAL. The HAL instructs the physical camera sensor to write raw pixels directly into that exact physical RAM address.

Your app then takes that exact same file descriptor and hands it to the Video Encoder. Hardware inside the encoder reads the raw pixels directly from RAM and compresses them into an H.264 video stream. The main CPU never touches the pixel data at all. This zero-copy pipeline makes recording high-resolution video on a battery-powered device possible.

Allocating from ION in Practice

You need a practical way to request these specialized buffers from user-space code. Interaction with ION happens through a standard device node located at /dev/ion.

Opening this device node allows you to pass your allocation requirements directly to the kernel. You define a specific data structure that specifies the size you need and a bitmask of the acceptable heaps. You then send this structure using standard input/output control commands.

struct ion_allocation_data alloc_data;
alloc_data.len = 20 * 1024 * 1024; 
alloc_data.heap_id_mask = ION_HEAP_SYSTEM_MASK; 
alloc_data.flags = 0;

int ion_fd = open("/dev/ion", O_RDONLY);
ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data);

Common Mistake: Forgetting to close the ION file descriptor when you are done with the buffer will cause a severe memory leak. The physical RAM will not be freed until the file descriptor count reaches zero.

After this system call, your application holds a file descriptor representing a block of hardware-ready memory. You can map this into your process for reading or hand it off to a hardware driver for zero-copy operations.

ION transformed how Android handles hardware memory by centralizing allocations. Hardware components get exactly the physical memory traits they need through specialized heaps. File descriptors then act as universal tickets, enabling zero-copy pipelines where data flows directly between chips without touching the CPU.

While ION served as the standard for years, managing custom heaps from every hardware vendor eventually overwhelmed the Linux kernel maintainers. The entire ecosystem needed a way to standardize this zero-copy philosophy without the fragmentation. That pressure led to the adoption of DMA-BUF, which changes how Android shares memory today.