dlmalloc由Doug Lea编写的内存分配算法

(1)mspace_malloc/mspace_free

(2) dlmalloc/dlfree

1.边界标记

2.空闲块分箱:2个分箱数组

(1)小块空闲块大小(0-256):数组元素-空闲块链表

(2)大块空闲块大小(>256):数组元素-空闲块

3. 空闲段

内存分配过程:查找空闲内存

1.小块内存(1)相应链表---->清出链表,返回(空闲块中数据)内存地址

(2)所有链表---->清出

(3)

(4)上次分配剩余空闲块(找到后分割剩下的)---->清出链表,分割

(5)空闲段

(6)系统分配内存

2.大块内存(1)

(2)上次分配剩余空闲块

(3)空闲段

(5)系统分配内存

内存释放过程:

1.边界标记

Dlmalloc将内存分成很多块,并且采用所谓的边界标记法对内存进行管理,在Dlmalloc的实现源码中定义了两种结构体malloc_chunk 和malloc_tree_chunk,从它们的定义中可以看到结构体malloc_tree_chunk除了比malloc_chunk多三个字段以外,前四个字段和malloc_chunk完全一样。这两种结构体主要用于对内存块按大小进行不同的管理。
struct malloc_chunk {
  size_t               prev_foot;  /* Size of previous chunk (if free).  */
  size_t               head;       /* Size and inuse bits. */
  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;
};
typedef struct malloc_chunk  mchunk;
typedef struct malloc_chunk* mchunkptr;
typedef struct malloc_chunk* sbinptr;  /* The type of bins of chunks */
struct malloc_tree_chunk {
  /* The first four fields must be compatible with malloc_chunk */
  size_t                    prev_foot;
  size_t                    head;
  struct malloc_tree_chunk* fd;
  struct malloc_tree_chunk* bk;
  struct malloc_tree_chunk* child[2];
  struct malloc_tree_chunk* parent;
  bindex_t                  index;
};

我们先来看看只考虑使用结构体malloc_chunk管理内存的情况(内存都被分为小块,32位机器上即是256字节以下,而对于结构体 malloc_tree_chunk,其管理的是大块,32位机器上即是256字节以上):

按照边界标记法,结构体malloc_chunk通过字段head和prev_foot将内存分割成很多块,从图中①所示,可以看到某结构体内的 prev_foot是记录的前一个chunk块的信息(事实上是前一个chunk块的大小),因此我们可以利用如下宏:
#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) ))
来获得前一个chunk块的malloc_chunk结构体指针。
指针fd、bk只有当该chunk块空闲时才存在,其作用时用于加入到空闲chunk块链中统一管理,而如果该chunk块被分配给应用 程序使用,那么这两个指针也就没有用(该已经 chunk块从空闲链中拆出)了,所以也当作应用程序的使用空间,而不至于浪费,图中②所示。
head字段记录与本chunk块的相关信息,这包括本chunk块大小,本块是否在使用中,前一chunk块是否在使用中。
head一个字段就能存储这么多信息是因为Dlmalloc在分割内存的时候总是以地址对齐(默认是8字节,可以自由设置,但是8字节是最小值并且设置的值必须是2为底的幂函数值,即是alignment = 2^n,n为整数且n>=3)的方式来进行的,所以用head来存储本chunk块大小字节数的话,其末3bit位总是0,因此这三位可以用来存储其它信息,比如:
以第0位作为标志位,标记前一chunk块是否在使用中,为1表示使用,为0表示空闲。
以第1位作为标志位,标记本chunk块是否在使用中,为1表示使用,为0表示空闲。
我们来看看它们的各自相关判断代码:
#define SIZE_T_ONE          ((size_t)1)
#define SIZE_T_TWO          ((size_t)2)
#define PINUSE_BIT          (SIZE_T_ONE)
#define CINUSE_BIT          (SIZE_T_TWO)
#define cinuse(p)           ((p)->head & CINUSE_BIT)
#define pinuse(p)           ((p)->head & PINUSE_BIT)
对于前面说的prev_foot字段,也利用了其一个空闲位用于标记该chunk块是否右mmap分配,于此类似,所以就不多说了,感兴趣的可以查看源码。
对于结构体malloc_tree_chunk,其实在内存分割上合结构体malloc_chunk完全一致,因为它们的前四个字段完全一样(事实上只有两个字段prev_foot和head起边界标记作用),其它的字段都是用于空闲链管理的。
本篇简单的把Dlmalloc如果按照边界标记法分割内存描述了一下,下篇继续两种空闲链的各自管理分析。

二.空闲块分箱

(1)对于大小在 256 字节以下的 chunk 块是通过 malloc_chunk 组织管理的, 256 字节 以下的chunk块一共有256/8=32类,即字节为8字节、16字节、24字节、32字节,……,256字节,因此dlmalloc维护32个双向环形链表(而且具有链表头节点,加头节点的最大作用就是便于对链表内节点的统一处理,即简化编程),每一个链表里的各空闲chunk块的大小一致,因此当应用程序需要某个字节大小(这里的字节大小是考虑了chunk块头和对齐等所占空间了的,即如果应用如果程序调用函数malloc( 8 ),那么到dlmalloc这应该比8大,这种更细节的疑问下面还有,并非我故意不表达,只是转述太多了,倒说不清我自己真正想说的了,阅读时请读者自己注意就好)的内存空间时直接在对应的链表内取就可以了(具体稍有不同,即如果对应链表内没有空闲可用chunk块,则还会查看下一个链表,举个例子:当应用程序申请32个字节,如果32字节大小的链表为空,那么dlmalloc还会在大小为40字节的链表内查找空闲chunk块。),这样既可以很好的满足应用程序的内存空间申请请求而又不会出现太多的内存碎片。我们可以用如下图来表示dlmalloc对256字节以下的空闲chunk块组织方式。

(2)对于大小在256字节以上的chunk块,dlmalloc同样也采用了所谓的分箱机制,不过由于大于256的数目有很多,因此这里的分箱不能够像对于0到256这个有限区间的分箱来得简单。

dlmalloc程序使用 smallbins 数组来记录这 32个双向环形链表表头,该字段定义在结构体malloc_state内,在这里我们先不管malloc_state结构体而只关注 smallbins 这个字段,它的定义如下:
struct malloc_chunk {
size_t
prev_foot;
/* Size of previous chunk (if free).
*/
size_t
head;
/* Size and inuse bits. */
struct malloc_chunk* fd;
/* double links -- used only if free. */
struct malloc_chunk* bk;
};
mchunkptr
smallbins[(NSMALLBINS+1)*2];
最后,至于为什么是 66 个数组元素而不是 64 或 65 ,这个仔细想想也好理解,好了,就到这,下篇继续吧。
其中,mchunkptr在上一小节已经提过,为“typedef struct malloc_chunk* mchunkptr;”,而宏NSMALLBINS为32,即是“#define NSMALLBINS (32U)”。因此smallbins为一个具有66个malloc_chunk结构体指针元素的数组,为什么是66个呢?不是32就可以了么?这里 Doug Lea使用了一个技巧,如果按照我们的常规想法,也许会申请32个malloc_chunk结构体指针元素的数组,然后再给链表申请一个头节点(即32 个),再让每个指针元素正确指向而形成32个具有头节点的链表。事实上,对于malloc_chunk类型的链表“头节点”,其内的prev_foot和 head字段是没有任何实际作用的,因此这两个字段所占空间如果不合理使用的话那就是白白的浪费。我们再来看一看66个malloc_chunk结构体指针元素的数组占了多少内存空间呢?结果为66*4=264字节。而32个malloc_chunk类型的链表“头节点”需要多少内存呢?32*16=512,真的是512么?不是,刚才不是说了,prev_foot和head这两个字段是没有任何实际作用的,因此完全可以被重用(覆盖),因此实际需要内存为32*8=256。264大于256(事实上前8个字节被浪费掉了),那么这66个malloc_chunk结构体指针元素数组所占内存空间就可以存储这32个头节点了,事实上Doug Lea也是这么做的。我们来看下于此相关的这一句代码:

#define smallbin_at(M, i)   ((sbinptr)((char*)&((M)->smallbins[(i)<<1])))

其中的sbinptr也是个malloc_chunk结构体指针类型(typedef struct malloc_chunk* sbinptr;),M表示前面提到的结构体malloc_state,各位仔细体会一下这句代码中的强制转换就会理解这种技巧了。

DLmalloc 内存分配算法相关推荐

  1. Linux 操作系统原理 — 内存 — 内存分配算法

    目录 文章目录 目录 前文列表 内存碎片 伙伴(Buddy)分配算法 Slab 算法 虚拟内存的分配 内核态内存分配 vmalloc 函数 kmalloc 用户态内存分配 malloc 申请内存 用户 ...

  2. linux为系统分配内存,Linux操作系统知识讲解:走进Linux 内存分配算法

    Linux 内存分配算法 内存管理算法--对讨厌自己管理内存的人来说是天赐的礼物 1.内存碎片 1) 基本原理 产生原因:内存分配较小,并且分配的这些小的内存生存周期又较长,反复申请后将产生内存碎片的 ...

  3. 嵌入式操作系统内核原理和开发(内存分配算法)

    内存分配是操作系统必须面对的一个环节,除非这个系统本身不需要内存安排,所有业务可以通过全局数据和堆栈搞定.内存分配其实不困难,但是由内存引申出来的东西就比较复杂了.早前没有MMU,系统本身的空间和用户 ...

  4. 嵌入式操作系统内核原理和开发(改进的链表内存分配算法)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 之前我自己也写过基于链表的内存分配算法,但是看了rawos的内存分配算法,还是感觉rawos写 ...

  5. 嵌入式操作系统内核原理和开发(最快、最优、最差内存分配算法)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面我们说到了基于链表的内存分配算法.但是之前我们也说过,其实内存分配一般有三个原则,最快.最 ...

  6. 各种存储分配算法java代码实现_Java实现操作系统中四种动态内存分配算法:BF+NF+WF+FF...

    1 概述 本文是利用Java实现操作系统中的四种动态内存分配方式 ,分别是:BF NF WF FF 分两部分,第一部分是介绍四种分配方式的概念以及例子,第二部分是代码实现以及讲解. 2 四种分配方式 ...

  7. Java实现内存分配算法 FF(首次适应算法) BF(最佳适应算法)

    一.概述 因为这次os作业对用户在控制台的输入输出有要求,所以我花了挺多的代码来完善控制台的显示. MemoryAlgorithm类里只是和控制台输入输出有关的操作,而对内存的所有逻辑操作都是用Mem ...

  8. 操作系统-动态内存分配算法

    内存分配算法 1.首次适应算法(FF) 2.循环首次适应算法(NF) 3.最佳适应算法(BF) 4.最坏适应算法(WF) 本程序使用前端技术实现(html+css+JavaScript) 新建以下文件 ...

  9. Java实现操作系统中四种动态内存分配算法:BF+NF+WF+FF

    1 概述 本文是利用Java实现操作系统中的四种动态内存分配方式 ,分别是: BF NF WF FF 分两部分,第一部分是介绍四种分配方式的概念以及例子,第二部分是代码实现以及讲解. 2 四种分配方式 ...

最新文章

  1. 一个男人和一个女人的故事
  2. Android客户端捕获http请求包的方法
  3. 基础的重要性(程序猿之路)
  4. DeepMind哈萨比斯对话哈里王子:2018年AI最大的突破在生物或化学 2017-12-29 新智元 新智元报道 编辑:刘小芹 胡祥杰 【新智元导读】BBC 电台第四台连续第14年在
  5. 浅谈分布式存储系统数据分布算法
  6. 除了工作怎么交朋友_夫妻感情不好怎么办?夫妻关系紧张该如何解决?
  7. 在使用ToolBar + AppBarLayout,实现上划隐藏Toolbar功能,遇到了一个坑。
  8. 7.组件连线(贝塞尔曲线)--从零起步实现基于Html5的WEB设计器Jquery插件(含源码)...
  9. 第四节:5种数据类型在TypeScript中的运用
  10. 什么叫补仓,什么是补仓
  11. H5 37-背景缩写
  12. java并发编程实战读书笔记 ExecutorCompletionService
  13. java算法题解法_LeetCode算法题-Ugly Number(Java实现-四种解法)
  14. qq空间显示手机型号android,qq说说显示手机型号 qq说说显示手机型号在哪里设置...
  15. 使用MATLAB计算切比雪夫多项式系数
  16. “我与正大光明眼科集团共成长”主题演讲比赛精彩回顾
  17. 如何区分静态网页与动态网页
  18. 龙ol服务器维护补偿boss,《龙OL》12月17日更新公告
  19. 续航超1000km,极氪成为宁德时代麒麟电池全球量产首发品牌 | 美通社头条
  20. mybatisplus where或and后面的条件用括号括起来

热门文章

  1. 查找字符串中首个非重复字符
  2. 区分Activity的四种加载模式(转)
  3. Android开源介绍-UI组件
  4. 23_传智播客iOS视频教程_类的对象的创建
  5. Scrum 项目7.0
  6. win8.1下jdk的安装和环境变量的配置 eclipse的安装和汉化
  7. 当Linux提权不能反弹Shell时利用metasploit进行提权
  8. 实现日志管理的两种方式:aop、拦截器
  9. Impala架构和工作原理
  10. 洛谷 2585 [ZJOI2006]三色二叉树——树形dp