内存字节池控制块 TX_BYTE_POOL

tx_byte_pool_id

内存字节池id

tx_byte_pool_name

内存字节池名字

tx_byte_pool_available

内存字节池可分配字节

tx_byte_pool_fragments

内存字节池块个数

tx_byte_pool_list

内存字节池头list指针

tx_byte_pool_search

内存字节池开始搜索起始地址

tx_byte_pool_start

内存字节池所在内存空间起始地址

tx_byte_pool_size

内存字节池大小

tx_byte_pool_owner

内存字节池所属线程

tx_byte_pool_suspension_list

挂起线程链表

tx_byte_pool_suspended_count

tx_byte_pool_created_next

指向下一个内存字节池指针

tx_byte_pool_created_previous

指向前一个内存字节池指针

tx_byte_pool_huge_memory_start

tx_byte_pool_min_available

tx_byte_pool_available_list

tx_mutex_ptr

tx_huge_memory_watermark

  • 内存池链表

所有创建的内存字节池都插入到created_ptr。tx_byte_pool_created_next指向下一个内存字节池,tx_byte_pool_created_previous指向前一个内存字节池。

  • 内存字节池初始化

内存字节池初始化为一个大的块和一个小的控制块两部分,小的控制块只是为了fisrt-fit算法,不会存储数据使用。(first-fit首次适应算法:从链首开始查找,直至找到一个能满足其大小要求的空闲分区为止。然后再按照要求的大小,从该分区中划出一块内存分配给请求者,余下的空闲分区仍留在空闲分区链中。特点: 该算法倾向于使用内存中低地址部分的空闲区,在高地址部分的空闲区很少被利用,从而保留了高地址部分的大空闲区。显然为以后到达的大作业分配大的内存空间创造了条件。缺点:低地址部分不断被划分,留下许多难以利用、很小的空闲区,而每次查找又都从低地址部分开始,会增加查找的开销。)

内存池中不同块(block)之间通过单向链表连接,利用每块内存头部前8个字节作为控制字段,前4个字节指向下一个内存块的起始地址,随后4个字节作为内存是否分配标志,如果已经被分配,指向内存池TX_BYTE_POOL控制块指针,如果空闲设置为TX_BYTE_BLOCK_FREE。

最后一块内存池只有8个字节,作为控制块,前4个字节指向内存池TX_BYTE_POOL控制块指针,后4个字节设置为TX_BYTE_BLOCK_ALLOC。

如下图,pool_ptr指向TX_BYTE_POOL内存池控制块,整个内存分为两块,TX_BYTE_POOL中tx_byte_pool_start指向内存池首地址。第一块中的前4个字节存储第二块(最后一块,控制块)的起始地址,后四个字节设置为TX_BYTE_BLOCK_FREE,因为还没有使用,标记为空闲块。

第二块(最后一块,小的控制块)的前4个字节存放指向内存池起始地址tx_byte_pool_start,后4字节标记为TX_BYTE_BLOCK_ALLOC 标记为已分配,也表示最后一块。

tx_byte_pool_search指向搜索内存时的开始地址。

这样内存块就构成了单向循环链表。

  • 内存分配

内存分配使用首次适应(fisrt-fit)算法,从上次操作的空闲内存块开始查找,找到大小合适内存,返回成功。

找到第一个大于请求分配大小的内存块时,并把这个大的内存块分为两个内存块,前一个作为分配到的内存使用,后一个挂入内存管理链表中。

初始化时,内存块只有两个,随着分配内存和释放内存,会出现很多小的内存块,称为内存碎片。

查找过程中,发现两个邻居内存块是地址连续的,那么把这两个内存块合并成为一个内存块,称为内存整理。

如果内存池没有足够可用内存,申请分配内存的线程会挂起。

举例:

下图为第一次分配内存后,最初的第一块内存被分为了两块:第一块和第二块。

第一块作为分配到的内存使用。memptr跨过前八字节的控制字段指向内存的地址,为返回给程序的地址。第一块的前4字节存储第二块内存起始地址,随后4个字节指向了内存池控制块,标志着第一块内存已经被占用,不是空闲内存了。

第二块内存前4个字节指向第三块内存(最后一块内存),继续构成单向链表。随后4个字节存储TX_BYTE_BLOCK_FREE,表示还是空闲内存块。

tx_byte_pool_search指向搜索内存时的开始地址,第一块已经被占用,所以指向了第一个空闲的内存块,即第二块内存起始地址。

如下图,第二次分配内存。第一个空闲的内存块(第二块)开始搜索,找到了合适大小,由于请求大小小于空闲的内存块大小,所以把空闲块再次分为两块,如下图所示。

第二块是分配给应用程序的,memptr为返回地址,第二块内存的前4字节指向第三块内存首地址,随后4个字节指向了内存池控制块,表示这块内存已经被占用。

第三块内存,前4字节指向第四块内存(最后一块内存),继续构成单向链表。随后4个字节存储TX_BYTE_BLOCK_FREE,表示还是空闲内存块。

  • 内存释放

内存释放时,根据释放内存首地址,减8,计算出内存块控制字段的首地址,通过前四字节找到内存池控制块指针,可以进行内存释放操作。

释放后,4到8字节存储TX_BYTE_BLOCK_FREE表示为空闲内存块。

这里第二块内存和第三块内存虽然是连续的,但并没有进行合并或内存整理。什么时间合并呢?

等到申请内存分配,发现请求大小大于第二块的大小,开始查找第三块时,这时发现第二块和第三块内存连续,就把第二块和第三块合并为一块。

释放内存后,会检查suspension list中是否有线程,如果有,尝试分配内存并恢复线程执行。线程恢复是按照FIFO顺序恢复,并没有按照线程优先级高低顺序。但是可以在线程释放前调用_tx_byte_pool_prioritize,把最高优先级线程移动到挂起链表最前面,从而先恢复最高优先级线程。

  • 内存整理

内存在多次分配和释放后,可能会出现大量小的内存块,这种想象成为内存碎片化。当需要分配一个较大内存时,每次可能需要先遍历大量小内存,这样会使查找开销增加,算法性能下降。由于每个内存块都占用8个字节的控制字段,大量小内存会导致内存的浪费。

查找过程中,发现两个邻居内存块是地址连续的,那么把这两个内存块合并成为一个内存块,称为内存整理。内存整理能够提升算法性能,但提升有限。其它优化方式,可以从查找算法进行优化,如二叉树查找等。

举例:

假设下图为多次内存分配和释放后的结构,第一块被占用,第二块,第三块,第四块为空闲内存块。

第二块大小为64字节,第三块大小为64字节,第四块内存大小为256字节。假如现在申请分配内存大小为128字节,在分配查找过程中,发现第二块太小不满足,但第二块和第三块地址连续,于是把第二块和第三块合并为一块,并且发现合并后正好满足请求,返回给应用程序,如下图。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值