内存管理

本文说明 Solution 内存管理的关键接口、配置入口和常见使用场景。

1. 参考代码

文件

作用

solution\framework\service\srv_app\mem\app_mem.c

提供应用内存申请、释放和统计接口

solution\framework\service\srv_app\mem\app_bmem.c

管理大块内存申请和释放

solution\framework\service\srv_app\mem\app_tlsf.c

基于 TLSF 管理应用动态内存池

solution\framework\__template__\project\__applicaiton_private__\app_mem_custom.c

给项目定制内存区域和分配策略

2. 芯片内存配置基础

内存配置需先明确芯片硬件特性,核心关注 核结构内存类型存储支持,具体如下:

2.1 核结构与内存分配

  • 核类型: 芯片分为 单核(如 52x 系列)和 双核(含低功耗小核 LCPU + 高性能大核 HCPU )。

    • 双核场景:需根据功能对性能/功耗的需求分配核资源(如低功耗任务放在 LCPU,高性能任务放在 HCPU )。

    • 单核场景(52x 系列):硬件为单核,但软件架构虚拟“LCPU”(实际运行在 HCPU 上),确保跨平台代码兼容性。

  • 内存类型: 包括 SRAM(高速、小容量)和 PSRAM(低速、大容量)。

    • LCPU 通常仅挂载 SRAM;部分 SF32LB5x 系列 LCPU 可能挂载 NOR Flash(需根据功能需求选择芯片型号 )。

2.2 存储方案差异

SF32LB5x 系列支持两种存储方案,内存配置需适配:

2.2.1 NOR Flash 方案

  • 特性:支持 XIP(直接执行),内存需求较低。

  • 说明:代码和资源可直接从 NOR Flash 运行,无需额外搬运。

2.2.2 NAND Flash/eMMC 方案

  • 特性:不支持 XIP,需注意以下两点:

    1. PSRAM 容量需 ≥8MB(用于加载代码和资源 );

    2. 代码(code.bin)和图片资源需从 NAND Flash/eMMC 搬运到 PSRAM 后才能执行/访问。

3. 内存分配与配置

内存主要用于 9 类场景,其存储位置(SRAM/PSRAM)通过链接脚本(如 solution\framework\__template__\config\hcpu\linker_script\link_flash_52x.sct)配置,具体如下:

No

内存用途

说明与配置方式

1

Code/RO

必须常驻内存的代码及只读数据,由链接脚本指定存储位置

2

RW/ZI

可读写数据(RW)和初始化为 0 的数据(ZI),默认随代码分配

3

帧缓冲区(framebuffer)

用于 GUI 显示,配置路径:menuconfig (Top) LVGL config

4

堆内存(Heap)

包括系统堆(system heap)和专用堆(memheap),配置路径:menuconfig Memory Config

5

场景动画切换 buffer

支持应用切换动画时所需缓冲区,配置路径:menuconfig (Top) Gui Framework Config Trans Animation,申请方式见app_mem.c

6

线程栈(Thread stack)

建议静态分配,频繁使用栈放SRAM(减少Cache换入换出提升性能)

7

图形加速资源

图片旋转缓存从 SRAM 堆分配,绘图 canvas 从 PSRAM 堆分配

8

NAND Flash/eMMC 的 code.bin

因不支持 XIP,开机时从 NAND Flash/eMMC 搬运到 PSRAM 执行

9

NAND Flash/eMMC 的图片资源

需先搬运到 PSRAM 才能被 ePIC 硬件访问,需使用 PSRAM 堆

4. 堆内存(Heap)详解

Solution 在 app_mem.c 中提供多种堆内存管理接口,包括块内存、系统堆及专用堆,特性如下:

4.1 块内存(可选)

  • 配置:通过menuconfig (Top) Memory Config Using block memory开启。

  • 特性

    • 优点:分配速度快,无内存碎片;

    • 缺点:需预先定义块大小和数量,复用性低,易浪费内存。

  • 适用场景:映射 lv_mem(LVGL 琐碎内存分配 ),通过 app_malloc 申请(申请失败时自动转用系统堆 ),建议应用小内存优先使用。

4.2 系统堆(system heap)

  • 分配接口rt_malloc

  • 存储位置:可配置在 SRAM 或 PSRAM(通过menuconfig (Top) Memory Config Sysheap config in PSRAM指定)。

  • 管理方式

    • RT_USING_MEMHEAP_AS_HEAP未被定义: 小内存管理,此种模式下内存利用率高,但分配速度慢(适合 SRAM,因容量有限 );

    • RT_USING_MEMHEAP_AS_HEAP被定义: 大内存管理,此种模式下分配速度快,但易产生碎片(适合 PSRAM,因容量较大 )。

  • 配置系统堆Size

    • SYS_HEAP_IN_PSRAM未被定义: 自动计算,系统堆Size = SRAM总Size - 已经分配的SRAM大小(静态变量,包括栈)

    • SYS_HEAP_IN_PSRAM被定义:通过menuconfig (Top) Memory Config Sys heap size in PSRAM配置。

4.3 SRAM memheap / PSRAM memheap

  • 分配接口app_cache_alloc(需指定 CACHE_SRAMCACHE_PSRAM );

  • 特性:采用大内存管理(分配快,易碎片 );

  • 适用场景

    • SRAM 堆用于高速度需求的内存(受容量限制 );

    • PSRAM 堆用于消息存储、图片缓存、canvas 等。

  • :PSRAM 堆大小自动计算,无需手动配置。

  • 配置

    • SRAM HEAP:通过menuconfig (Top) Memory Config Cache in sram size(bytes)配置。

    • PSRAM HEAP:在app_mem.c中通过#define PSRAM_HEAP_SIZE (PSRAM_BASE + PSRAM_SIZE - PSRAMHEAP_BASE)自动计算。

      • 计算原理:PSRAM总的大小 - 已经分配的PSRAM大小(静态变量和动态的专用heap)

4.4 Freetype memheap

  • 分配接口ft_malloc

  • 用途:专用于字体渲染(支持 Freetype 时需配置 )。

  • 容量建议

    • NAND Flash/eMMC 方案:建议 ≥500KB;

    • NOR Flash 方案:建议 ≥200KB。

  • 配置:通过menuconfig (Top) Memory Config FREETYPE_CACHE size(bytes)指定堆大小。

4.5 TLSF 内存管理(可选)

TLSF(Two-Level Segregated Fit,双级隔离适配)是一种面向实时系统的动态内存分配算法。它按块大小建立一级、二级空闲链表,并用位图记录可用链表;分配时通过位图快速定位满足请求的空闲块,释放时与相邻空闲块合并。因此,分配和释放的执行时间更稳定,适合 UI、图片缓存、协议栈等频繁申请和释放不同大小内存的场景。

  • 覆盖范围:启用后,TLSF 替换系统堆及所有通过 rt_memheap_init 初始化的 memheap 的底层分配实现,包括 SRAM、PSRAM 和 Freetype memheaprt_malloc 先尝试系统堆,再尝试已加入系统堆链表的 memheap;当前 app_mem.c 会将 PSRAM memheap 加入该链表,因此 PSRAM 可作为系统堆的后备空间。

  • 不覆盖的机制:块内存由 bmem_alloc 独立管理,TLSF 不改变其块大小、块数量和分配规则。app_malloc 仍优先申请块内存;块内存分配失败后,才调用 TLSF 接管的 rt_malloc

  • 适用场景:长时间运行且存在大量不同尺寸动态内存申请/释放,缺省内存管理出现碎片、分配耗时波动或大块内存申请失败时,可评估启用。

  • 注意事项:TLSF 不能修复内存泄漏、越界或释放后使用等问题;它会在每个堆起始处存放控制信息,并保留每个分配块的管理头,因此小块频繁申请时仍有元数据开销。启用前应确认各堆容量余量,启用后需回归验证高频分配、页面切换和长时间运行场景。

  • 与其他配置的关系:TLSF 与 USING_BLOCK_MEM 可同时使用,二者并不互斥;启用 TLSF 后,MSG_CACHE_SIZE 配置项不可用,由 TLSF 统一管理对应动态分配需求。

menuconfig 配置步骤

  1. 执行 menuconfig,进入 menuconfig (Top) Memory Config

  2. Use TLSF as memory managment 设为 y,生成宏 APP_USING_TLSF_MEM

  3. 根据产品内存方案继续配置 Sysheap config in PSRAMSys heap size in PSRAMPSRAM_CACHE size(bytes)Cache in sram size(bytes);TLSF 不替代这些堆容量与存储位置配置。

  4. 重新编译并烧录。启用后可在 FinSH 中执行 list_memheap 查看各堆总量、已用量和峰值;执行 list_tlsf_heap 查看 TLSF 块布局和空闲块状态。

5. 为某个工程单独定义块内存大小

块内存默认定义在app_mem.c中,

#ifndef APP_BMEM_SELF_DEFINED
    #if defined (USING_BLOCK_MEM) && defined (BSP_USING_PSRAM)
        #if defined (SOLUTION_RES_USING_NAND)
            #define _X      200
        #else
            #define _X      0
        #endif
        PSRAM_RET_SECT_BEG;
        ALIGN_4
        BMEM_REGISTER(16, (400 + _X));
        BMEM_REGISTER(32, (200 + _X));
        BMEM_REGISTER(48, (200 + _X));
        BMEM_REGISTER(64, (200 + _X));
        BMEM_REGISTER(100, (200 + _X));
        PSRAM_RET_SECT_END
    #endif
#endif

app_mem.c属于框架类c程序,如果客户定义了多个产品,一些产品对块内存的配置不一样,则可以通过如下的步骤为某个工程单独配置块内存:

  1. 通过menuconfig (Top) Memory Config User-defined block memory size定义APP_BMEM_SELF_DEFINED

  2. copy __template__\project\__applicaiton_private__目录到在project/hcpu下面,修改app_mem_custom.cBMEM_REGISTER中的参数block大小以及个数。

6. 内存头信息

内存的管理,app_mem.c在RTT的内核函数的基础上做了一层封装,内存申请/释放主要依赖两个RTT的文件mem.cmemheap.c

6.1 rt_xxx (malloc/free/calloc/realloc)

依赖于是否打开宏RT_USING_MEMHEAP_AS_HEAP

  • 如果RT_USING_MEMHEAP_AS_HEAP定义,则rt_malloc/rt_free/rt_calloc/rt_realloc定义在memheap.c,此时内存头为 struct rt_memheap_item类型

   struct rt_memheap_item
   {
       rt_uint32_t             magic;                      /**< magic number for memheap */
       struct rt_memheap      *pool_ptr;                   /**< point of pool */
       rt_uint32_t size;                                   /**< requested memory size excluding header*/
   #ifdef RT_USING_MEMTRACE
       //rt_uint8_t thread[4];                               /* thread name */
       rt_uint32_t ret_addr;
       rt_tick_t   tick;
       //rt_uint32_t dbg_magic;
   #endif
   #ifdef MEM_ASYN_FREE
       rt_uint16_t             ref_count_magic;
       rt_uint16_t             ref_count;
   #endif
       struct rt_memheap_item *next;                       /**< next memheap item */
       struct rt_memheap_item *prev;                       /**< prev memheap item */

       struct rt_memheap_item *next_free;                  /**< next free memheap item */
       struct rt_memheap_item *prev_free;                  /**< prev free memheap item */
   };

Solution默认是打开RT_USING_MEMTRACE,板子上默认是打开MEM_ASYN_FREE的。因此,此时头信息总共有40个字节(未打开MEM_ASYN_FREE则为36个字节)。

  • 如果未被定义,则rt_malloc/rt_free/rt_calloc/rt_realloc定义在mem.c中,此时内存头为struct heap_mem *类型

   struct heap_mem
   {
       /* magic and used flag */
       rt_uint16_t magic;
       rt_uint16_t used;

       struct heap_mem *next, *prev;
       rt_uint32_t size;       /**< requested memory size excluding header*/
   #ifdef RT_USING_MEMTRACE
   #ifdef RT_MEM_RECORD_THREAD_NAME
       rt_uint8_t thread[4];   /**< thread name */
   #endif  /* RT_MEM_RECORD_THREAD_NAME */
       rt_uint32_t ret_addr;
       rt_tick_t   tick;
   #endif
   #ifdef MEM_ASYN_FREE
       rt_uint16_t ref_count_magic;
       rt_uint16_t ref_count;
   #endif
};

Solution默认是打开RT_USING_MEMTRACE(但RT_MEM_RECORD_THREAD_NAME未打开),板子上默认是打开MEM_ASYN_FREE的。因此,此时头信息总共有28个字节(未打开MEM_ASYN_FREE则为24个字节)。

6.2 rt_memheap_xxx (alloc/free/calloc/realloc)

定义在memheap.c,此时内存头为 struct rt_memheap_item类型。

6.3 app_xxx (malloc/free/calloc/realloc)中申请的块内存

定义在app_bmem.h,此时内存头为struct struct_bmem_tbmem_item_t类型。

7. 内存信息查看

在恢复现场后,在trace32中装入:

  • sdk\tools\crash_dump_analyser\script\show_heap.cmm: 显示通过mem.c中函数申请的内存

  • sdk\tools\crash_dump_analyser\script\show_rt_memheap.cmm:显示通过memheap.c中函数申请的内存

  • sdk\tools\crash_dump_analyser\script\show_bmem.cmm:显示已经申请但未释放的块内存

装入路径为:trace32 -> File -> Run Script -> sdk\tools\crash_dump_analyser\script\xxx.cmm

8. 内存异常导致的死机及原因

常见内存异常场景如下(需重点规避 ):

  1. 内存越界:分配后使用范围超出申请大小。

  2. 野指针操作:未初始化/未申请的指针直接使用,覆盖其他内存或访问非法地址。

  3. 非保持区内存误用:睡眠唤醒后未重新初始化 non-retention 区域内存,继续使用。

  4. 退栈异常:野指针导致函数返回时栈错误。

  5. 回调函数指针异常:指针未赋值或指向非法地址。

  6. 释放后使用:内存已 rt_free或·app_cache_free 释放,仍继续访问。

  7. 多线程互斥缺失:同一片内存被多线程并发访问,数据异常。

  8. Cache 操作错误:未执行 mpu_dcache_cleanmpu_dcache_invalidate,导致数据不一致。