Memory Management in FreeRTOS

Scan to FollowLearn Embedded Together, learn and grow together

Memory Management in FreeRTOS

This is a series of introductory articles on FreeRTOS, aimed at helping beginners quickly get started and master the basic principles and usage methods of FreeRTOS while organizing their own knowledge.

Quick Start with FreeRTOS – An Introduction to the System

FreeRTOS Official Chinese Website is Now Live

FreeRTOS Coding Standards and Data Types

Quick Start with FreeRTOS – Task Management

FreeRTOS Learning – Detailed Explanation of Message Queues

FreeRTOS Learning – Counting Semaphores

FreeRTOS Learning – Mutex Semaphores

FreeRTOS Learning – Event Groups

FreeRTOS Learning – Task Notifications

FreeRTOS Learning – Software Timers

This article introduces the memory management related content of FreeRTOS.

FreeRTOS provides a flexible memory management mechanism that allows developers to choose the most suitable memory allocation strategy based on different application scenarios and hardware resources.

Memory management in FreeRTOS has the following characteristics:

  1. Portability: The memory management interface is separated from the specific implementation
  2. Customizability: Provides multiple memory management algorithms and supports user-defined implementations
  3. Determinism: Some implementations provide deterministic allocation times
  4. Fragmentation Control: Provides strategies to reduce memory fragmentation

Memory Management Architecture

Memory Management Hierarchy

FreeRTOS memory management is divided into three levels:

  1. Hardware-Dependent Layer: Handles memory layout specific to the MCU and memory initialization at startup
  2. Kernel Memory Management Layer: Memory allocation for FreeRTOS kernel objects (tasks, queues, semaphores, etc.)
  3. Application Memory Management Layer: Memory allocation for user applications

Memory Management Interface

FreeRTOS defines a unified memory management interface (in portable.h):

void *pvPortMalloc(size_t xSize);
void vPortFree(void *pv);
size_t xPortGetFreeHeapSize(void);
size_t xPortGetMinimumEverFreeHeapSize(void);

Five Memory Management Implementations

FreeRTOS source code provides five memory management implementations, located in the <span>FreeRTOS/Source/portable/MemMang</span> directory:

heap_1.c – The Simplest Implementation

Characteristics:

  • Only allocates, does not free
  • Deterministic execution time
  • Small code size

Implementation Principle:

  • Simply divides memory into a static array
  • Uses a pointer to track remaining space

Applicable Scenarios:

  • Applications that only allocate memory at startup and do not free it afterwards
  • Systems with high determinism requirements

API Behavior:

  • <span>pvPortMalloc()</span>: Works normally
  • <span>vPortFree()</span>: Does not perform any operation (empty function)

heap_2.c – Best Fit Algorithm

Characteristics:

  • Supports allocation and deallocation
  • Uses the best fit algorithm
  • Does not merge adjacent free blocks

Implementation Principle:

  • Divides the heap into a block linked list
  • Each block contains metadata and user data area
  • Finds the closest free block to the requested size during allocation

Disadvantages:

  • Can produce memory fragmentation
  • Allocation time is not deterministic

Applicable Scenarios:

  • Applications that require dynamic allocation and deallocation, but with relatively fixed allocation sizes
  • Has gradually been replaced by heap_4.c

heap_3.c – Standard Library Wrapper

Characteristics:

  • Wraps the standard library’s malloc() and free()
  • Adds thread safety protection

Implementation Principle:

  • Protects malloc/free operations with mutex locks
  • Directly calls the malloc/free provided by the compiler

Applicable Scenarios:

  • Platforms with mature standard library implementations
  • Need to integrate with other code using the standard library

heap_4.c – First Fit Algorithm with Fragmentation Merging

Characteristics:

  • Supports allocation and deallocation
  • Uses the first fit algorithm
  • Merges adjacent free blocks to reduce fragmentation
  • Deterministic execution time (if using the same size allocation)

Implementation Principle:

  • Maintains a linked list of free blocks sorted by address
  • Uses the first fit algorithm during allocation
  • Checks and merges adjacent free blocks during deallocation

Advantages:

  • Effectively reduces memory fragmentation
  • Higher allocation efficiency

Applicable Scenarios:

  • Applications that require frequent dynamic allocation of different sized memory
  • Recommended choice for most general applications

heap_5.c – Supports Non-contiguous Memory Regions

Characteristics:

  • Has all the features of heap_4
  • Supports multiple non-contiguous memory regions
  • Requires explicit initialization of memory regions

Implementation Principle:

  • Uses the algorithm of heap_4
  • Maintains information about multiple memory regions

Applicable Scenarios:

  • Hardware with memory distributed across multiple non-contiguous regions
  • Need to manage different types of memory (e.g., fast RAM and slow RAM) uniformly

Configuration and Usage

Configuring FreeRTOS Memory Management

Configure in FreeRTOSConfig.h:

#define configTOTAL_HEAP_SIZE ((size_t)1024*25) // Define total heap size
#define configAPPLICATION_ALLOCATED_HEAP 0      // Heap memory allocated by the compiler (1 means user allocated)
#define configUSE_MALLOC_FAILED_HOOK 0         // Whether to enable malloc failure hook function

Selecting Memory Management Implementation

Include the corresponding memory management source file in the project (e.g., heap_4.c)

Custom Memory Management

Developers can fully customize memory management:

  1. Implement pvPortMalloc() and vPortFree()
  2. Optionally implement xPortGetFreeHeapSize() and other auxiliary functions
  3. Set in FreeRTOSConfig.h:
#define configAPPLICATION_ALLOCATED_HEAP 1
extern uint8_t ucHeap[configTOTAL_HEAP_SIZE]; // User-defined heap memory

Usage Recommendations

Heap Size Planning

Monitor heap usage:

printf("Free heap: %u, Min ever free: %u\n", 
       xPortGetFreeHeapSize(),
       xPortGetMinimumEverFreeHeapSize());

Consider:

  • All task stack space
  • Kernel objects (queues, semaphores, etc.) usage
  • Application dynamic memory requirements
  • Reserve a safety margin (usually 20-30%)

Strategies to Reduce Memory Fragmentation

Try to use static allocation:

StaticTask_t xTaskBuffer;
StackType_t xStack[configMINIMAL_STACK_SIZE];
xTaskCreateStatic(...);
  1. Uniformly allocate sizes or use memory pools
  2. Avoid frequent allocation/deallocation of different sized memory blocks

Debugging Tips

Enable malloc failure hook:

void vApplicationMallocFailedHook(void) {
    // Handle memory allocation failure
}

Use FreeRTOS built-in memory statistics feature:

#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1

Regularly check heap usage

Advanced

Memory Protection (MPU) Support

For ARM Cortex-M processors with MPU:

#define configENABLE_MPU 1
#define configTOTAL_MPU_REGIONS 8

Thread Local Storage (TLS)

FreeRTOS supports task-specific memory pointers:

void *pvTaskGetThreadLocalStoragePointer(TaskHandle_t xTask, BaseType_t xIndex);
void vTaskSetThreadLocalStoragePointer(TaskHandle_t xTask, BaseType_t xIndex, void *pvValue);

Stack Overflow Detection

Method 1: Stack Filling Mode Detection

#define configCHECK_FOR_STACK_OVERFLOW 1

Method 2: MPU Protection Detection

#define configCHECK_FOR_STACK_OVERFLOW 2

Common Issues and Solutions

Memory Allocation Failure

Possible Causes:

  • Insufficient heap size
  • Severe memory fragmentation
  • Memory leaks

Solutions:

  • Increase configTOTAL_HEAP_SIZE
  • Switch to a more efficient memory management algorithm
  • Check and fix memory leaks

Memory Leaks

Detection Methods:

  • Regularly check xPortGetFreeHeapSize()
  • Use professional memory analysis tools
  • Implement memory tracking wrappers

Preventive Measures:

  • Establish ownership rules for each allocation
  • Use RAII pattern (Resource Acquisition Is Initialization)
  • Establish clear memory release responsibilities

Performance Issues

Optimization Strategies:

  • For frequently allocated small objects, use memory pools
  • Consider static allocation of critical objects
  • Select appropriate memory management algorithms

Application Examples

Custom Memory Management Implementation

#define CUSTOM_HEAP_SIZE (1024 * 32)
static uint8_t ucCustomHeap[CUSTOM_HEAP_SIZE];
static size_t xNextFreeByte = 0;

void *pvPortMalloc(size_t xWantedSize) 
{
    void *pvReturn = NULL;
    
    if((xNextFreeByte + xWantedSize) < CUSTOM_HEAP_SIZE) 
    {
        pvReturn = &ucCustomHeap[xNextFreeByte];
        xNextFreeByte += xWantedSize;
    }
    
    return pvReturn;
}

void vPortFree(void *pv) 
{
    // Simple implementation does not support freeing
}

Memory Pool Implementation

#define POOL_BLOCK_SIZE 32
#define POOL_BLOCKS 100

typedef struct 
{
    uint8_t ucBlock[POOL_BLOCK_SIZE];
    BaseType_t xInUse;
} MemoryBlock_t;

static MemoryBlock_t xMemoryPool[POOL_BLOCKS];

void *pvGetMemoryBlock(void) 
{
    for(int i = 0; i < POOL_BLOCKS; i++) 
    {
        if(!xMemoryPool[i].xInUse) 
        {
            xMemoryPool[i].xInUse = pdTRUE;
            return xMemoryPool[i].ucBlock;
        }
    }
    return NULL;
}

void vReturnMemoryBlock(void *pv) 
{
    for(int i = 0; i < POOL_BLOCKS; i++) 
    {
        if(xMemoryPool[i].ucBlock == pv) 
        {
            xMemoryPool[i].xInUse = pdFALSE;
            break;
        }
    }
}

Optimization

  1. Allocation Time Analysis:
  • heap_1 and heap_4 have deterministic times for the same size allocation
  • heap_2 and heap_5 have non-deterministic times
  • Memory Overhead Comparison:
    • heap_1: No management overhead
    • heap_2/heap_4: About 8-16 bytes of metadata per block
    • heap_5: Additional region management overhead
  • Low Power Design:
    • Reduce the number of dynamic memory allocations
    • Use static allocation for critical resources
    • Avoid memory operations in low power modes

    Conclusion

    FreeRTOS provides a flexible and diverse memory management solution, allowing developers to choose the most suitable implementation based on application needs.

    For most application scenarios, heap_4.c is the recommended default choice, offering good performance and fragmentation control capabilities.

    In resource-constrained or special requirement systems, other implementations or custom memory management can be considered.

    Key Recommendations:

    1. Fully understand the memory usage patterns of the application
    2. Closely monitor memory usage during the development phase
    3. Reserve sufficient memory margin for production systems
    4. Consider using static allocation to improve system determinism
    5. Establish strict memory management standards to prevent memory leaks and fragmentation issues

    Memory Management in FreeRTOS

    Follow 【Learn Embedded Together】 to become better together.

    If you find this article useful, click “Share”, “Like”, or “Recommend”!

    Leave a Comment