EmbeddedRelated.com

Heap

Category: Memory | Also known as: heap memory

The heap is a region of memory used for dynamic allocation, where blocks of memory are requested and released explicitly at runtime via functions such as malloc/free (C) or new/delete (C++). Unlike the stack, heap memory has no automatic lifetime; each allocation persists until explicitly freed or until the program ends.

In practice

On bare-metal embedded targets, the heap is typically a fixed-size region carved out of RAM at link time, defined by linker script symbols such as _heap_start and _heap_end. The allocator (often a simple first-fit or best-fit implementation supplied by the C runtime, such as a dlmalloc-derived allocator in newlib-based toolchains) manages a free-list within that region. On RTOS-based systems, the RTOS often provides its own heap, for example FreeRTOS offers five heap implementations (heap_1 through heap_5) with different fragmentation and thread-safety characteristics, replacing or supplementing the standard C allocator.

Dynamic allocation is used in embedded systems for data structures whose size is not known at compile time, such as variable-length message queues, plugin registries, or runtime-parsed configuration. However, many safety-critical and resource-constrained designs avoid or restrict heap use entirely. Unpredictable allocation latency, fragmentation over time, and the difficulty of proving worst-case memory usage make the heap a liability in hard real-time or certified (IEC 61508, DO-178C) contexts. MISRA-C guidelines, for instance, strongly discourage dynamic memory allocation from the heap, and depending on the edition and project configuration, rules may restrict or effectively prohibit it after initialization.

Heap fragmentation is one of the most common long-term failure modes in embedded systems that do run dynamic allocators. As mixed-size blocks are allocated and freed repeatedly, the allocator can be left with enough total free bytes but no contiguous region large enough to satisfy a new request, causing malloc to return NULL even when aggregate free memory appears sufficient. This problem grows worse over long uptimes and is hard to reproduce in short test runs.

Diagnosing heap issues requires instrumentation. Wrapping malloc/free to track peak usage, block counts, and allocation call sites is a practical first step; the blog post "How to make a heap profiler" covers this technique in detail. On targets with an MPU (memory protection unit), such as ARMv7-M parts like the STM32F4, placing a guard region at the heap boundary can convert silent heap overflows into detectable faults.

Discussed on EmbeddedRelated

Frequently asked

Why do many embedded projects avoid the heap entirely?
Three main reasons: fragmentation can cause non-deterministic allocation failures after long runtimes; allocation and free operations have variable and sometimes unbounded execution time, which violates hard real-time requirements; and it is difficult to statically prove worst-case RAM usage when allocations depend on runtime behavior. Many teams use static or stack allocation exclusively, or allocate from the heap only during initialization and never free.
What happens when malloc returns NULL on a bare-metal system?
On a bare-metal target there is no OS to handle the out-of-memory condition. If the calling code does not check the return value and dereferences the NULL pointer, the behavior is undefined. On cores with an MPU configured to fault on address 0 (common on Cortex-M parts), this typically triggers a MemManage fault, BusFault, or HardFault depending on the specific core configuration and bus behavior; without MPU protection, the system may silently corrupt memory. Always check the return value of malloc, and consider providing a custom failure handler.
How is the heap different from the stack?
The stack is used for automatic storage: local variables and function call frames are pushed and popped in LIFO order, and their lifetimes are tied to scope. The heap is used for dynamic storage: lifetimes are explicit and programmer-controlled. Stack allocation is typically a single pointer adjustment and is very fast; heap allocation involves searching a free-list and has higher, variable overhead. Stack overflows are also a common embedded failure mode, covered in the blog post 'Are We Shooting Ourselves in the Foot with Stack Overflow?'
How do RTOS heap implementations differ from the standard C library heap?
RTOS-provided allocators are typically thread-safe by design and may offer simpler, more predictable behavior suited to embedded constraints. FreeRTOS heap_1, for example, only supports allocation with no free, making it trivially safe from fragmentation but inflexible. heap_4 and heap_5 support free with coalescing. The standard C library allocator (e.g., newlib's malloc) is general-purpose and may not be thread-safe without locking shims, and its footprint can be larger. Mixing the RTOS heap and the C library heap in the same project can also cause confusion about which pool is being drawn from.
How can I measure heap usage on an embedded target?
A common approach is to wrap malloc and free with thin instrumentation that tracks the current allocated total, peak allocated bytes, number of live allocations, and optionally the call site (via __FILE__/__LINE__ or return address). The blog post 'How to make a heap profiler' describes this technique. Some RTOSes expose native APIs for heap statistics, such as FreeRTOS's xPortGetFreeHeapSize() and xPortGetMinimumEverFreeHeapSize(). Filling the unallocated heap with a known pattern (e.g., 0xCD) at startup and scanning it at runtime is another low-cost watermark method.

Differentiators vs similar concepts

The heap is sometimes conflated with general RAM or with the BSS/data segments. BSS holds zero-initialized global and static variables; the data segment holds initialized globals; the stack holds automatic (local) variables. The heap is the portion of RAM reserved for dynamic allocation, typically a fixed region carved out at link time rather than all remaining unused RAM. The heap is also distinct from memory pools: a memory pool is a fixed-size block allocator, usually implemented on top of a static array, that avoids fragmentation by restricting all allocations to one block size. Memory pools are often preferred over a general-purpose heap in embedded systems precisely because they offer O(1) allocation with no external fragmentation, though internal memory waste is still possible depending on pool design.