# xmem **Repository Path**: schips/xmem ## Basic Information - **Project Name**: xmem - **Description**: 一个轻量级的内存管理组件,支持内存动态分配、回收与越界检测。 - **Primary Language**: C - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://my.oschina.net/xGavin/blog/4486150 - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 17 - **Created**: 2020-11-25 - **Last Updated**: 2023-05-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # xmem ![XMEM](https://img2020.cnblogs.com/blog/2120938/202010/2120938-20201026203252906-1319700645.png) 一个轻量级的内存管理组件,支持内存动态分配、回收与越界检测,只需简单的配置就能轻松导入工程,使用及其方便,可以有效地提高研发测试效率。 同时,xmem支持多种个性化操作,可以更高效更便捷的管理内存。 xmem的设计支持Header分离模式,可以配置XMEM_HEADER_DIVIDE使能Header分离模式。Header分离模式内存块管理头与内存块采用相向生长模式,避免因内存写越界,内存块管理头信息出错而导致整个程序崩溃的问题。 xmem支持超块(super block),可以配置XMEM_SUPERBLOCK_SUPPORT使能Super Block功能。使用一个头管理多块小内存(cell),这样可以大大减小头开销。 xmem支持越界检测, 可以配置XMEM_CORRUPT_CHECK开启越界检测。共Header模式检查Header的值是否合法,Header分离模式检查各内存块的barrier。 > Ultra-lightweight dynamic memory allocation in C. ## How to use 1. 配置临界区进入宏XMEM_ENTER_CRITICAL,与临界区退出宏XMEM_EXIT_CRITICAL。 2. 在内存里定义一个数组,用作xmem内存池。 3. 调用xMemInit初始化xmem。 4. 调用xmalloc分配内存,调用xfree释放内存。 ... ## License Apache License V2.0 ## 描述 > 原贴:[xmem内存管理](https://my.oschina.net/xGavin/blog/4486150) xmem是一个轻量级的内存管理组件,支持内存动态分配、回收与越界检测,只需简单的配置就能轻松导入工程,使用及其方便,可以有效地提高研发测试效率。同时,xmem支持多种个性化操作,可以更高效更便捷的管理内存。 最初打算写一个动态内存分配组件是多年前在一个WiFi项目上遇到了内存的瓶颈,要联网对接云端,需要用到很多组件,而这些组件很多都需要用到动态内存分配,而且同时还要求保持多个TCP连接。不仅如此,有些场合需要开辟大的缓冲区,例如OTA,采用静态内存分配编译直接挂掉。但是合同已签,甲方已付款,并且还指定了WiFi芯片,看起来,采用动态内存分配是唯一解决问题的办法。 通过分析,我发现在OTA的时候无需进行其他操作,OTA的缓冲区与Flash的页大小一致,减去OTA的缓冲区编译可以通过,并且内存空间还稍有富余,说明采用动态内存分配的方法可行。然而,我们拿到的SDK很多资源是不开放的,又难以取得原厂的支持,在迫不得已的情况下只好自己造个轮子。 我定下的需求是占用ROM空间小,内存利用效率高。**最简单的内存管理方式,就是分配一大块RAM空间作为动态内存池,内存池由内存块链接而成。每个内存块又可以分作两部分,起始部分称为内存块头,记录内存块的大小、分配状态和下一块内存的地址,余下部分为可分配区域**。起初,内存池只有一个空闲的大内存块,随着一次次的内存分配,大内存块不断地裂开成许多小的被分配的内存块,再随着这些小的内存块的释放,又合并成大的空闲的内存块,周而复始,一切都是那么完美。 ![](https://img2020.cnblogs.com/blog/2120938/202010/2120938-20201026203252891-1977325357.png) 然而,有的时候捣蛋鬼总是在不经意间登门拜访,小内存块分配就是一个这样的捣蛋鬼,在JSON解析时会经常遇到。JSON的Key通常都是较短的字符串,为分配一小块内存需要消耗更大的空间去建立内存块头,解析一个长的JSON报文的时候,内存很快被耗尽,这感觉就像那啥榴莲,壳比肉重。**为解决这个壳比肉重的问题,小内存块组闪亮登场,从内存池里分配一块内存,然后又把这块内存均分成许多小内存块,这些小内存块共用一个内存块头,每一个小内存块都对应一个分配标记位,这样就节省了不少的内存空间。同时,针对不同的小内存块需求,可以建立多个小内存块组,4、8、16等,小于等于4字节的统一分配4字节,大于4字节小于等于8字节的统一分配8字节,大于8字节小于等于16字节的统一分配16字节。** ![](https://img2020.cnblogs.com/blog/2120938/202010/2120938-20201026203252904-905982601.png) 福无双至,祸不单行。解决了捣蛋鬼的问题,接着是这个令人头疼的内存访问越界问题。内存访问越界的危害就是软件会莫名其妙的出现一些问题,而更多时候内存块头因为物理上紧邻前一块内存的被分配区域,发生写越界的时候会被写覆盖,破坏了内存块链表从而导致系统的崩溃。为此,**加入写越界检测的功能可以在研发测试阶段暴露问题,将出现写越界问题的内存地址前后内存块内的值输出,有助于找到出问题的地方并修正**。 然而,研发测试阶段不出问题并不等于量产后不出问题,一个健壮的系统需要具有一定的容错性,万一在量产的产品上遇到内存写越界的问题,如果能将关键运行数据保存起来Reset再恢复运行,而不是崩溃,将大大提高产品的可靠性。为此,关键之处是将内存块头保护起来,将内存块头与分配区域分离是一个不错的选择。通常,内存写越界都是从低地址往高地址方向写。因此,**把内存块头置于低地址区,分配区域置于高地址区,并采取相向生长的策略,可以大大减少因从低地址往高地址方向写越界而导致内存块头被写覆盖的问题,使系统不至于因为内存写越界而崩溃**。 ![](https://img2020.cnblogs.com/blog/2120938/202010/2120938-20201026203252890-237492857.png)