7.14. MALLOC

Overview

本章节主要提供 1、查询内存信息的方法;2、内存泄露检测手段;3、踩内存检测手段。

示例演示:

    1. 查询内存信息的方法

    1. 内存泄露检测手段

    1. 踩内存检测手段

7.14.1. 查询内存信息的方法

- 基础概念:

内存信息包括内存池大小、内存使用量、剩余内存大小、最大空闲内存、内存水线、内存节点数统计、碎片率等。

内存水线:即内存池的最大使用量,每次申请和释放时,都会更新水线值,实际业务可根据该值,优化内存池大小;

碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-100.0*最大空闲内存块大小/剩余内存大小)来度量;

- 配置

  1. 进入 demo_config.h ,开启宏 USE_MALLOC_TEST_DEMO

  2. 进入 app_config.h 增加 #define RTOS_STACK_CHECK_ENABLE ,打印周期默认为60秒,测试建议设置为3秒;

  3. 使用mem_heap库来定位不断打印定位整个SDK申请内存的情况,以便计算碎片率,此方法使用mem_heap内存管理才有效,工程增加apps/common/system/mem_heap.c后重新编译下载即可

- 示例演示

  1. malloc_stats(); //打印一下当前内存使用情况

  2. malloc_dump(); //打印堆内存的使用情况和碎片率

其中,total:总共内存堆可使用的大小;left:当前剩余可使用的大小;max:水线值;fragment:内存碎片率

7.14.2. 内存泄露检测手段

- 基础概念

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

有两种方法查找内存泄漏, 推荐使用第二种方法

- 方法 1:定位一个功能模块内部有没有内存泄漏

  • 1.需要在 app_config.h 增加 #define MEM_LEAK_CHECK_ENABLE

  • 2.如果需要启动内存泄漏侦测, 必须所有需要侦测的.c和.h文件都要包含头文件 #include "system/mem_leak_test.h"

  • 3.例如打印: MALLOC LEAK DBG:malloc_test_task 20 rets:0x1c10660,size:0x1000 代表发现malloc_test_task函数的第20行,rets地址为0x1c10660,申请大小为0x1000,rets可以通过工具 cpu/wl80/tools/定位异常地址.bat 查看

- 方法 2:需要使用mem_heap库来定位不断打印定位整个SDK申请内存的情况来分析,下面将详细介绍如何使用该方法进行内存泄露侦测。

  1. 进入 demo_config.h ,开启宏 USE_MALLOC_TEST_DEMO

  2. 请参考 1、 查询内存信息的方法 中的 配置 3和4。

- 示例演示(针对方法 2进行示例演示)

1. 在 app_main.c.app_main() 中添加如下代码,模拟内存泄露场景。

printf("my leak mem = 0x%x -------\r\n\r\n\r\n",malloc(5*1024*1024));
malloc_stats();
u32 leak_test = 1;
while(leak_test){
    leak_test += malloc(64);
}

2. 通过串口打印工具,查看测试结果

发现在地址0x2005002处,频繁申请大小为58字节的内存(注:之所以是58字节而不是64字节,是因为系统在进行内存管理时,会插入一定长度头部字段),通过 cpu/wl82/tools/定位异常地址.bat 定位该异常的地址,为 app_main.c.app_main()

7.14.3. 踩内存检测手段

- 基础概念

踩内存,即访问了不合法的地址。踩内存检测机制可用于检测动态内存池的完整性,及时发现是否发生踩内存问题。

- 配置

  1. 进入 demo_config.h ,开启宏 USE_MALLOC_TEST_DEMO

  2. 工程增加apps/common/system/mem_heap.c后重新编译下载。

- 示例演示

  1. app_main.c.app_main() 中添加如下代码,通过memset构造越界访问,踩到下节点的4个字节

int *q = (int*)malloc(1*sizeof(int));
int *k = q -1;
memset(k, 0,8);
mem_heap_check(__func__,__LINE__);  //使用mem_heap_check()进行踩内存检测
free(q);

2. 通过串口打印工具,查看测试结果

再通过 cpu/wl82/tools/定位异常地址.bat 输入rets_addrs定位异常地址,即在 app_main.c.app_main() 中发生了踩内存。

7.14.3.1. 常见问题

  • 内存使用导致死机的情况有哪些?

    答: 用户需要排查有没有错误使用内存申请的情况,例如:

    • 1.读写操作的地址范围超出内存申请指针包含的范围

    • 2.内存指针释放了还继续使用

    • 3.多次释放同一个内存申请的指针

  • 感觉到内存被篡改但是没有马上造成异常的情况应该如何定位?

    答: 需要使用mem_heap.c来定位, 需要在流程上存在篡改可能性的地方加入mem_heap_check函数来分析

  • 有时候发现内存堆空间足够,但是内存申请失败的原因是什么?

    答: 由于内存碎片导致没有一块连续的并且足够大内存可以申请导致的, 用户使用内存申请注意不要申请过于小size的内存,另外常驻内存不要使用malloc,直接使用静态变量,malloc多个变量的情况考虑优化成一次性malloc一个结构体使用

  • 打印出来中文乱码?

    答: 更换打印软件,有可能是该打印软件的转码格式不支持。

7.14.4. API Reference

Functions

void *malloc(size_t size)
void *zalloc(size_t size)
void *calloc(size_t count, size_t size)
void *realloc(void *rmem, size_t newsize)
void free(void *mem)
void *kmalloc(size_t size, int flags)
void *vmalloc(size_t size)
void vfree(void *addr)
void *kzalloc(unsigned int len, int a)
void kfree(void *p)
void malloc_stats(void)
void malloc_dump(void)
void ram_free(void *rmem)
void *ram_malloc(unsigned long size)
void *ram_realloc(void *rmem, unsigned long newsize)
void mem_heap_check(const char *const func, unsigned int line)
void memory_init(void)
void mem_stats(void)
size_t xPortGetFreeHeapSize(void)
size_t xPortGetMinimumEverFreeHeapSize(void)
size_t xPortGetPhysiceMemorySize(void)
void *get_physic_address(u32 page)
void *vmem_get_phy_adr(void *vaddr)