内存管理: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)

内存管理:TLSF算法原理分析_第1张图片

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(内部碎片最优化):

内存管理:TLSF算法原理分析_第2张图片

常规思路是:找到能满足内存请求大小的最小空闲块,就会有下面的流程(以搜索大小为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]对应的空闲块链中每一块都能满足需求,不需要检索空闲块链表找到最小的,而是直接取空闲块链中第一块即可。整体上还不会造成太多碎片。

你可能感兴趣的:(深入理解Linux内核,算法,开发语言,linux)