内存管理:TLSF算法原理分析

1、动态内存分配DSA:

动态内存分配(DSA)在计算机中十分重要,其主要用于在程序运行时,根据需要分配和释放内存。

(1)、DSA的几个要点分别为:
  • 内存管理方式:动态内存分配与静态内存分配 相对应,静态内存分配是在程序编译时为变量分配固定大小的内存空间,而动态内存分配是在程序运行时根据需要动态调整内存空间。

  • 内存分配函数:编程语言通常提供内置的内存分配函数,如C/C++中的malloccalloc和C++中的new,用于在堆(heap)中分配内存。这些函数返回一个指向分配内存的指针。

  • 内存释放:动态分配的内存必须在使用完后进行释放,以防止内存泄漏。释放内存的函数是C/C++中的free和C++中的delete。不释放已分配的内存会导致程序内存占用增加,最终可能导致系统性能下降。

  • 内存泄漏:如果分配的内存在使用后没有被释放,就会发生内存泄漏。这会导致内存逐渐耗尽,最终可能导致程序崩溃或系统不稳定。

  • 碎片问题:频繁的动态内存分配和释放可能导致内存碎片问题。分为外部碎片(内存块之间的未使用空间)和内部碎片(已分配内存块内部未使用的部分)。

  • 动态数据结构:DSA允许在运行时创建动态数据结构,如链表、树和图。这些数据结构的大小和形状可以根据程序的需求动态变化。

  • 性能开销:与静态内存分配相比,动态内存分配需要更多的系统开销,包括内存分配表的维护、碎片整理等。因此,在某些情况下,需要权衡是否使用动态内存分配。

  • 错误处理:动态内存分配可能因为内存不足或其他原因失败。程序应该对分配失败进行适当的错误处理,避免崩溃或不可预料的行为。

(2)、DSA的几个关键点为:
  • 快速响应时间(Fast response time):响应时间,理解为平时当申请分配size大小内存时,算法从池中查找到合适内存块的耗时,当然越短越好。
  • 有界响应时间(Bounded response time):内存分配最坏的响应时间,比如使某空闲list算法,在一些情形下直到遍历到最后一个节点,才能发现合适的内存块,比正常找到慢非常多。这种边界性超长响应对一些实时要求高的系统或软件并不友好。
  • 高效内存使用(Efficient Memory Use):内存的高效使用,这是最复杂且综合设计,必涉及内存碎片化的处理。比如内存池总有1000个字节内存,运行到一段时间后,500字节占用,500字节空闲,但占用与空闲的内存刚好单字节一一交错。这时你会发现,虽然池里面总共剩余500字节,你确连2个字节的连续内存都分配不出来。
(4)、常见的分配策略:
  • Sequential Fit:最基础的算法,空闲链表法,所有内存空闲块都放到一个单向/双向list中,查找时会有边界响应问题。
  • Segregated Fit:对Sequential Fit进行改进,对所有内存块按其大小区间放到不同list中,这些list首地址组成array,查找速度更快,dlmalloc算法使用该策略。
  • Buddy System:对Segregate Fit算法的改进,更好的切割和合并效率,分配时效不错但内部碎片化问题比较严重,典型算法策略Binary Buddies,Fibonacci Buddies, Weighted Buddies,Double Buddies。
  • Indexed Fit:基于使用高级结构索引空闲内存块。典型算法策略:基于平衡树的“Best Fit”,基于笛卡尔树存储的Stephenson’s Fast-Fit等。在一些情况下,比Segregated系列效果更好。
  • Bitmap Fit:位图法,算是Indexed Fit算法的改进,使用小段内存表示的位图来确认内存块的占用或空闲,其通过降低缓存未命中的概率来提高响应时间。

2、TLSF算法:

(1)、TLSF简介:

TLSF(全称Two-Level Segregated Fit),两级隔离Fit内存分配器,是一款通用的动态内存分配,专门用于实时要求。

其有以下特点:

  • 算法复杂度为O(1);
  • 每次分配的开销极低(4字节);
  • 低碎片化;
  • 主要采用两级位图(Two-Level Bitmap)与分级空闲块链表(Segregated Free List)的数据结构管理动态内存池(memory pool)以及其中的空闲块(free blocks),用Good-Fit的策略进行分配。
(2)、分级空闲块链表(Segregated Free List):

分级空闲块链表(Segregated Free List)的设计思想是将空闲块按照大小分级,形成了不同块大小范围的分级,组内空闲块用链表链接起来。每次分配时先按分级大小范围查找到相应链表,再从相应链表挨个检索合适的空闲块,如果找不到,就在大小范围更大的一级查找,直到找到合适的块分配出去。

(3)、两级位图(Two-Level Bitmap):

使用位图的优势:

  • 节省存储空间:用1-bit表示某个区间范围大小的空闲块是否存在;
  • 位操作速度快:部分体系结构有加速特殊位操作的指令(如clz, ffs,fls)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E4GXR36j-1692942875116)(file://C:\Users\te199\AppData\Roaming\marktext\images\2023-08-25-13-43-17-image.png?msec=1692942197503)]

TLSF采用了两级位图(Two-Level Bitmap)来管理不同大小范围的空闲块链(free block lists)。 上图中包含三个虚线矩形框分别是:

  • 第一级位图(First-Level Bitmap),表示内存块的粗粒度范围,一般是2的幂次粒度(例如[ 2^n ~ 2^n+1 ])

  • 第二级位图(Secend-Level Bitmap)是一个数组,一级位图中的每一位对应这个数组的一项,表示内存块的细粒度范围(例如[ 2^n + 02^n-2 ~ 2^n + 12^n-2 ])

  • 第三个框是内存中真正的空闲内存块(free blocks)

(4)、Good-fit分配策略:
a、Best-fit(内部碎片最优化):

常规思路是:找到能满足内存请求大小的最小空闲块,就会有下面的流程(以搜索大小为69字节的空闲块为例)

  • 基于位运算找到请求大小所在的第一级位图(First-Level bitmap)对应的粗粒度范围([ 64 ~ 128 ]),也就是二级位图的索引

  • 在粗粒度范围内,根据二级位图索引检索第二级位图(Second-Level bitmap)得到细粒度范围([ 68 ~ 70 ])

  • 如上图所示,沿着右下角空闲块链表可以检索到69字节的那一块是Best-fit

Best-fit策略最主要的问题还在于第三步,仍然需要检索对应范围的那一条空闲块链表,存在潜在的时间复杂度。

b、Good-fit:

Good-fit思路与Best-fit不同之处在于,Good-fit并不保证找到满足需求的最小空闲块,而是尽可能接近要分配的大小。

还以上述搜索大小为69字节的空闲块为例,Good-fit并不是找到[68 ~ 70]这一范围,而是比这个范围稍微大一点儿的范围(例如[71 ~ 73])。这样设计的好处就是[71 ~ 73]对应的空闲块链中每一块都能满足需求,不需要检索空闲块链表找到最小的,而是直接取空闲块链中第一块即可。整体上还不会造成太多碎片。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TLSF(Two-Level Segregated Fit)内存分配算法是一种通用的动态内存分配算法,专门设计用于满足实时要求。它具有以下特点: 1. 算法复杂度为O(1):TLSF算法通过使用两级位图和分级空闲块链表的数据结构来管理动态内存池和其中的空闲块。这种设计使得malloc、free、realloc和memalign等操作的时间复杂度都为O(1),即常数时间。 2. 低碎片化:TLSF算法采用Good-Fit的策略进行内存分配,即选择最合适大小的空闲块进行分配。这样可以减少内存碎片化,提高内存利用率。 3. 支持动态添加和删除内存池区域:TLSF算法支持在运行时动态地增加或删除多块不连续的内存,将它们作为一个内存堆使用。这种灵活性使得TLSF算法适用于各种内存管理场景。 下面是一个使用TLSF内存分配算法的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include "tlsf.h" int main() { // 创建一个TLSF内存池 void* pool = malloc(1024 * 1024); // 分配1MB的内存作为内存tlsf_pool tlsf = tlsf_create_with_pool(pool, 1024 * 1024); // 分配内存 void* ptr1 = tlsf_malloc(tlsf, 100); // 分配100字节的内存 void* ptr2 = tlsf_malloc(tlsf, 200); // 分配200字节的内存 // 使用分配的内存 sprintf(ptr1, "Hello"); sprintf(ptr2, "World"); // 打印分配的内存 printf("ptr1: %s\n", (char*)ptr1); printf("ptr2: %s\n", (char*)ptr2); // 释放内存 tlsf_free(tlsf, ptr1); tlsf_free(tlsf, ptr2); // 销毁TLSF内存tlsf_destroy(tlsf); // 释放内存池 free(pool); return 0; } ``` 这段代码演示了如何使用TLSF内存分配算法进行内存分配和释放。首先,我们创建一个TLSF内存池,并使用tlsf_malloc函数分配内存。然后,我们使用分配的内存进行操作,并最后使用tlsf_free函数释放内存。最后,我们销毁TLSF内存池并释放内存池。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值