
Click on the aboveblue text to follow us
In embedded systems, memory resources are often very limited, and memory leaks can lead to degraded system performance or even crashes. A memory leak refers to memory allocated by a program that is not properly released, gradually exhausting available memory.

As a lightweight real-time operating system (RTOS), FreeRTOS is widely used in resource-constrained embedded devices, and its memory management mechanism provides developers with tools and methods to detect and prevent memory leaks.
First, let’s discuss the memory management mechanism of FreeRTOS.
FreeRTOS manages dynamic memory allocation through different heap implementations, located in the source code directory portable/MemMang.
Here are five heap implementations and their characteristics:

Since heap_1.c does not support freeing, memory leaks do not exist in the traditional sense. Therefore, this article focuses on heap implementations that support freeing (heap_2.c, heap_3.c, heap_4.c, and heap_5.c), as memory allocated in these implementations may lead to leaks if not released.
FreeRTOS provides two key functions to monitor heap usage, helping developers detect potential memory leaks:
- xPortGetFreeHeapSize: Returns the current remaining heap size (in bytes). By regularly checking this value, trends in heap usage can be observed. If the remaining heap size continues to decline without stabilizing, it may indicate a memory leak.
- xPortGetMinimumEverFreeHeapSize: Returns the minimum remaining heap size since the system started. This function helps identify peaks in heap usage and determine if memory exhaustion is approaching.
Below is a monitoring task that regularly logs heap usage:
void vMonitorHeapTask(void *pvParameters) { size_t xFreeHeapSize, xMinFreeHeapSize; for(;;) { xFreeHeapSize = xPortGetFreeHeapSize(); xMinFreeHeapSize = xPortGetMinimumEverFreeHeapSize(); printf("Current remaining heap: %u bytes, Historical minimum remaining heap: %u bytes\n", xFreeHeapSize, xMinFreeHeapSize); vTaskDelay(pdMS_TO_TICKS(1000)); // Log every second }}
FreeRTOS provides trace macros that allow developers to customize the logging of kernel and application events.
For memory management, the traceMALLOC and traceFREE macros can be used to track pvPortMalloc and vPortFree calls, helping to identify unfreed memory blocks.
In FreeRTOSConfig.h or application code, the following macros can be defined:
#define traceMALLOC(pvReturn, xSize) do { \ TaskHandle_t xCurrentTask = xTaskGetCurrentTaskHandle(); \ char *pcTaskName = pcTaskGetName(xCurrentTask); \ printf("Task %s allocated %u bytes at address %p\n", pcTaskName, xSize, pvReturn); \} while(0)
#define traceFREE(pv, xSize) do { \ printf("Freed address %p\n", pv); \} while(0)
The above code uses printf as an example. In actual embedded systems, it may be necessary to write logs to a buffer or output via serial port, depending on hardware support.
By checking the logs, developers can compare allocation and release records to look for unfreed memory blocks. For example, if an address appears in traceMALLOC but not in traceFREE, it may indicate a leak point.
By modifying the BlockLink_t structure in heap_4.c, fields can be added to record the handle or name of the allocating task. For example, Chris Hockuba’s article suggests maintaining an allocation list (like BlockLink_t* allocList[256]) to record each allocated memory block and its associated task.
Here is a simplified implementation:
void vPortAddToList(BlockLink_t *pxBlock) { for (int i = 0; i < 256; i++) { if (allocList[i] == NULL) { allocList[i] = pxBlock; break; } }}
void vPortRmFromList(BlockLink_t *pxBlock) { for (int i = 0; i < 256; i++) { if (allocList[i] == pxBlock) { allocList[i] = NULL; break; } }}
This method requires a deep understanding of the FreeRTOS source code and is suitable for advanced developers.
Memory leaks are sometimes related to buffer overflows. Canary values (fixed patterns) can be added to the beginning and end of allocated memory blocks, and regularly checked for overwriting. For example, in pvPortMalloc, an additional 4 bytes can be allocated for the tail canary value and verified upon release.
If you do not wish to modify the heap implementation, you can wrap pvPortMalloc and vPortFree at the application layer to record allocation information:
typedef struct { void *pvAddress; size_t xSize; const char *pcTaskName;} AllocationRecord;
#define MAX_ALLOCATIONS 100AllocationRecord xAllocations[MAX_ALLOCATIONS];int xAllocationCount = 0;
void *myMalloc(size_t xSize) { void *pvReturn = pvPortMalloc(xSize); if (pvReturn != NULL && xAllocationCount < MAX_ALLOCATIONS) { TaskHandle_t xCurrentTask = xTaskGetCurrentTaskHandle(); char *pcTaskName = pcTaskGetName(xCurrentTask); xAllocations[xAllocationCount].pvAddress = pvReturn; xAllocations[xAllocationCount].xSize = xSize; xAllocations[xAllocationCount].pcTaskName = pcTaskName; xAllocationCount++; } return pvReturn;}
void myFree(void *pv) { for (int i = 0; i < xAllocationCount; i++) { if (xAllocations[i].pvAddress == pv) { xAllocations[i] = xAllocations[xAllocationCount - 1]; xAllocationCount--; break; } } vPortFree(pv);}
void vPrintAllocations(void) { for (int i = 0; i < xAllocationCount; i++) { printf("Task %s allocated %u bytes at %p\n", xAllocations[i].pcTaskName, xAllocations[i].xSize, xAllocations[i].pvAddress); }}
This tracker records the address, size, and task name of each allocation, and can be checked for the current allocation status using vPrintAllocations.
Finally, to prevent and detect memory leaks, it is recommended to follow these practices:
- Prioritize static allocation: Use static creation functions like xTaskCreateStatic to avoid the risks of dynamic allocation.
- Ensure matching allocation and release: Every pvPortMalloc must have a corresponding vPortFree.
- Use memory pools: For fixed-size allocations, using memory pools can reduce fragmentation and leak risks.
- Regularly monitor the heap: Periodically check heap usage through monitoring tasks or tools.
- Code review: Review memory allocation code during development and testing phases to ensure logical correctness.

Clickto read the original text for more exciting content~