芯片中最为稀缺珍贵的每每是存储资源,为了更好的利用它们,开发者不得不变得吝啬分配、斤斤计较到每个字节。数组
FreeRtos V8.0.1针对动态内存分配提供了四种方案,分别放在heap一、heap二、heap三、heap4文件中。spa
heap1分配方法最为简单,代码量也相对较小,heap1只能申请不能回收,适合任务、队列等不须要执行删除操做的工程。线程
ucHeap占据的整块内存既为可申请的堆空间,空间大小由configTOTAL_HEAP_SIZE定义。索引
分配以前首先对堆的起始地址作偶对齐,这有可能会使一些起始的字节做为“占位”而丢弃掉,最终获得堆有效空间使用configADJUSTED_HEAP_SIZE代替。队列
(堆初始化)内存
Heap1分配时使用xNextFreeByte始终记录堆中可分配的首地址,起始值为0。资源
若初始化后须要申请20字节:开发
(申请20字节)源码
第二次申请30字节:内存管理
(申请30字节)
Heap2中每一个内存块都有对应结构体的记录链表和块大小,链表将空闲块按照块大小升序相连。Start和end结构体做为索引独立于堆区,end做为结束标识xBlockSize老是等于堆区总大小。
Heap2初始化堆区后图:
(heap2初始化后)
若是程序须要申请A、B、C三块内存,须要的空间大小为B>A>C,使用heap2的申请分配图以下:
(申请A空间)
(申请B、C空间)
Heap2回收时将内存块按照升序方式插入链表。假设剩余的D空间大小等于A,程序分别释放B、A、C内存,释放后堆区以下:
(释放B内存后)
(释放A内存后)
(释放C内存后)
内存链上的内存按照块大小递增,记录块信息的结构体随着分配逐渐增长,永远得不到释放。一旦程序频繁申请小块内存,即便释放后也没法申请大内存块。
Heap3使用了库中的malloc和free,此时堆区已经脱离freertos控制,configTOTAL_HEAP_SIZE成为无效参数。没有源码,,,,先不作分析~
Heap4中每一个内存块都有对应结构体的记录链表和块大小,链表将空闲块按照地址升序相连。Start结构体做为索引独立于堆区,end属于堆区一部分,因此去除一个结构体的size才是堆实际分配范围。
(heap4初始化后的堆区)
每当申请出空闲块后都会对应产生一个新的结构体,这些结构体会占据一部分堆空间。申请内存块A后堆区图:
(申请A内存后)
再次申请B、C内存块:
(申请B内存后)
(申请C内存后)
heap4在回收内存时会尝试将零散的内存块进行合并,减小碎片。
假设在申请A、B、C以后,B内存使用完毕须要释放掉,B块的上下都没有相邻的空闲内存,因此只插入空闲链表就能够完成释放。如图:
(释放B后)
释放A时,存在相邻的B区,两块内存会进行拼接,B块的结构体也被合并,如图:绿色区是被回收的内存
(释放A后)
回收C块时,上下都存在空闲区,回收工做分为两个步骤进行:
(释放C)
相同点
除heap3外,都使用大数组做为堆区进行分配,这看起来比较浪费,即便程序没有申请任何空间,堆区占据的内存在程序编译前就已经肯定。
Heap2与heap4都须要为记录内存块信息而消耗必定堆空间。
调用相同,统一使用pvPortMalloc和vPortFree,方便heap文件更换。
进行堆操做时都没有屏蔽中断,只是将调度器挂起。因此FreeRtos堆操做不能在中断里使用,若是必定要实现的话,线程中每个堆操做的地方如建立任务、信号量、队列等~都要先屏蔽中断,这会使FreeRtos调度器实时性降低。
区别
四种方式主要在分配方法上存在差别:
Heap1分配最为简单迅速,它适合只申请不释放的工程。
Heap2进行频繁申请释放后会形成较多的碎片,适合只申请固定大小内存的工程。
Heap4像是Heap2的升级版,分配方式几乎相同,在回收时添加了内存块拼接以尝试消除碎片。