这次我们深入了解malloc,malloc究竟分配多么大的空间,以及整个过程是如何进行的。

malloc其实并没有想象中那么低效,在底层的设计中也是充满着精妙之处的。

以下分解内容以VC6版本为基础。

main()的准备工作

下图展示了调用main之前编译器都做了什么。

按照从下往上的顺序,依次调用了1-7这些初始化程序准备函数。

在其中,又会有一些嵌套式的调用。我们可以看到在ioinit中调用了诸如malloc_dbg(),堆分配相关的初始化,以及非常重要的sbh相关函数。

SBH小区块管理

从heap_alloc_base()这段代码中就可以看出,在申请空间小于1016时会调用_sbh_alloc_block(),而其他空间大小则会直接使用系统的HeapAlloc

这里的sbh代表Small Block Heap,是对于申请堆区小空间的管理方式。

而这部分内容在VC10时,这些管理方式被包装到了OS里面。

SBH就是我们要分析的重点。

SBH之始

在开始时,系统会先建立一个_crtheap,初值为4096。

然后调用_sbh_heap_init(),分配配置SBH所需的管理空间。

可以看到这里分配了16个HEADER,类似链表的结构。

而之后动态配置中申请的空间就要与1016比较了,小于就从SBH取,大于就调用HeapAlloc()从_crtheap取

Header的结构如下:

其中Hi和Lo共有64位。Commit有32位。

还有一个指针以及一个结构体指针,这些在后面都会用到。

malloc()的真相

ioinit()申请空间

接下来就会进行一系列的内存分配与管理了。

首先调用的是ioinit(),这里会申请一些信息文件,大小为32*8=256 也就是16进制的100h

然后进入到debug模式

malloc就会对其进行包装。(这些包装都是为了debug做准备的)

debug模式包装

接下来就会在这100h的空间上下功夫。

首先整个区块的大小分为以下几块:

包括_CrtMemBlockHeader的大小,申请空间的大小以及无人区的大小。

这里就是动态申请,所以可以用这里的blocksize选择分配空间的方式(SBH还是系统)

_CrtMemBlockHeader是什么呢?其实就是我们以前所说的malloc分配的debug header。

但这次我们可以更清楚它是由什么组成的。

对照结构体的定义可以看出:

1和2是前后指针,用于连接多个block。

3指向的是申请这块空间的程序地址(所在的文件)。

4是记录申请使用该内存空间所在文件的行号

5是申请空间的实际大小,这里就是100h

6表示当前内存块的类型。这里有宏定义:

对应此处的是CRT块,也就是系统申请的内存。

7代表操作操作流水号

8则是无人区,一共上下共8个,大小是0xfd

这样无人区可以把实际申请内存空间像栏杆一样保护住。如果有内存越界发生可以在debug下及时发现。

这里的前后指针实现的效果如下:
就是将各个块连接在一起。

管理机制

在使用heap_alloc_base()申请空间时,如果符合小区块,那么会调用

sbh_alloc_block()

sbh_alloc_new_region()

sbh_alloc_new_group()

在sbh_alloc_block()里,会加入pad填充部分把大小调整至16的倍数,然后再加上下cookie(每个cookie大小为4字节)

这里的cookie记录总的大小,注意是131h而不是130h,这是因为16进制结尾都是0,如果某块分给了客户端,就用1代表分配出去了。

sbh_alloc_new_region()则实现了管理空间,真正开始分配。

管理中心

region,是内存的管理中心。

前面我们说过有16个HEADERS,而每一个HEADER都管理着这样一个空间。

先来看结构体:

region有一个char类型的数组,64位。最重要的是bitvGroupHi和bitvGroupLow,一共有32组,每组都是64位的。

还有一个tagGroup结构体,用来管理32个group

这个结构体如下:

除了有一个用于计数的cntEntires,还有64个tagListHead

tagListHead其实就是一个双向链表结构

所以这64个相当于32组。

最后整体就是这样的结构:

总览全局

结合每一个Header和所有申请的内存,我们可以得到这样的管理图。

先来看右边的一列,这是Header管理的空间但是是虚拟内存空间,只有当真正申请时才会真正分配。

而这些空间的总大小为1MB,共有32块,那么每块为32k

这32k会被分为8个page,每个page大小4k

然后将其首尾相接,交给管理中心。注意位置是在group0的最后一对前后指针。

**这里管理大于1k的内容。**因为1024k/16=64,刚好对应group的64个位置。所以大于1024的内容都交给最后一块管理。

这8个page也不是直接将4k串连起来,而是进行了一点处理。

每个部分为4096bytes,先分出两个边界(黄色部分),是2*4=8bytes,剩余4088,调整到16的倍数,为4080,剩余的8作为保留。

这里的黄色部分为回收做了准备。

而分配的空间,就来自于这些切割好的空白部分。

比如前面ioinit申请的30h的空间,就可以从这里分出去。

100h的实际申请空间+24h的debug header再加上两个cookies,总大小130h,这时对于进行debug header构造的程序来说,得到的地址就是这块空间的起始地址007d0ed0

内存分配流程

根据几张图,类似alloc那样来进行分配,看看发生了什么。

ioinit申请100h大小,区块大小130h,查找18号链表,发现无空闲。

先保留1MB的地址空间(没有分配),再实际分配32k的大小,分成8个page,再从page中分130h的空间。

对于一个page来说,还剩余ec0的空间。

管理中心是如何记录这些变化的呢?

首先,group0的cntEntries会+1,说明有一块空间被分配了出去。

然后,header中的32组64bytes的数据结构可以记录对应的链表信息。

比如这时只有最后一条链表连有数据块,所以对应的group0的最后一位置为1

继续申请240h的空间大小,由page分配,剩余c80

group0的cntEntries继续+1变为2,有两块空间被分配。

假设已经经历了14次分配,此时对应的cntEntries也变为了14

这时有一块空间需要回收,大小是240h,那么应该还给240h/10h=36-1=35号链表。于是就把这块空间的前后指针拉向#35。cntEntries-1 变为13

同时管理中心改变group0对应的值

表示第35号有连接数据块。

这里的前后指针依旧是嵌入式指针,在分配出去的时候是完整的空间,在回收时才会把前面的空间当作指针使用。

再申请b0的空间,按顺序检查链表,发现最近的有空间的链表是#35,所以从35号的空间中分出b0,剩余190。

cntEntries+1变为14

重新调整,把190h分给190h/10h=25-1=#24号链表。

同时修改管理中心的信息。

在经历了n次分配后,可能已经跳到了group1,管理中心对应的数据逐渐增加调整。

空间回收

malloc与free对应,而free并不是像alloc一样把区块据为己有,而是做到了真正的回收。

这时就用到了上下cookie。

cookie用来记录大小,但为什么要一对呢?因为当某一块为空时,直接根据其上下cookie就可以把上方和下方与之相连的空闲块都一次性回收。

free§在寻找区块时经历了以下过程:
首先看p在哪一个header中,再看p在哪一个group中,再看p在哪一个list中(看cookie)

这样的分段管理是非常利于归还内存的。

全回收

如何判断给出了几块空间,还有几块空间没收回?

还记得在group里有一个变量cntEntries,它可以记录有几块空间被分配了出去。

如果该值为0,那么说明空间已经全回收了。

但最后一块链表连接的8个page暂时不收回。为了预防下次再有申请内存的行为。直到出现两个全回收的group后,才会真正把所有空间归还。

实现方式是用_sbh_pHeaderDefer指针指向全回收的group所属的header,但暂时不释放,当第二个全回收group出现的时候去检查该指针的状态,如果为Defer那么就可以释放了。

malloc与allocator

我们知道了malloc的实现方式,那么与之前说的alloc这种设计有什么关联呢?

对于alloc来说,管理的区块最大是128bytes,而这么大的区块对于malloc来说就是SBH所管理的小区块。

malloc已经足够快了,单独设计并不是想要提升效率,而是为了减少cookie。

那一层一层的设计是否有些多余呢?

既然下层已经有这么精妙的设计,上层还有必要重新设计么?

当然是有的,抛开新的管理方式不谈,上层无法预测自己的使用场景,也就无法预测下层是否做了相应的管理,所以还是有必要的。

C++内存管理(4):malloc的秘密相关推荐

  1. 动态内存管理:malloc和free以及new和delete的联系与区别

    动态内存管理:malloc和free以及new和delete的联系与区别 文章目录 动态内存管理:malloc和free以及new和delete的联系与区别 一. C/C++中程序内存区域划分: 二. ...

  2. linux内存管理实验malloc,linux内存管理实验报告.doc

    linux内存管理实验报告 操作系统实验报告 院别:XXXXXX 班级:XXXXXX 学号:XXXXXX 姓名:稻草人 实验题目:内存管理实验 实验目的 通过本次试验体会操作系统中内存的分配模式: 掌 ...

  3. c语言malloc calloc,C语言内存管理:malloc、calloc、free的实现

    任何一个对C稍稍有了解的人都知道malloc.calloc.free.前面两个是用户态在堆上分配一段连续(虚拟地址)的内存空间,然后可以通过free释放,但是,同时也会有很多人对其背后的实现机制不了解 ...

  4. linux内存管理之malloc

    对于内核的内存管理,像kmalloc,vmalloc,kmap,ioremap等比较熟悉.而对用户层的管理机制不是很熟悉,下面就从malloc的实现入手.( 这里不探讨linux系统调用的实现机制. ...

  5. Linux中free函数头文件,Linux C 堆内存管理函数malloc()、calloc()、realloc()、free()详解...

    C 编程中,经常需要操作的内存可分为下面几个类别: 堆栈区(stack):由编译器自动分配与释放,存放函数的参数值,局部变量,临时变量等等,它们获取的方式都是由编译器自动执行的 堆区(heap):一般 ...

  6. C 这些东西的内存管理

    一.内存介绍 本文主要介绍C内存管理基本概念,以及C语言编译后的可执行程序的存储结构和执行结构. 在用户存储空间,一个C程序的在内存中的分配分类5大部分:代码段.全局已初始化数据段.bss段.堆和栈. ...

  7. UNIX再学习 -- 内存管理

    C 语言部分,就一再的讲内存管理,参看:C语言再学习 -- 再论内存管理  UNIX.Linux 部分还是要讲,足见其重要. 一.存储空间布局 1.我们先了解一个命令 size,继而引出我们今天要讲的 ...

  8. linux内核之内存管理.doc,linux内核之内存管理.doc

    Linux内核之内存管理 作者:harvey wang 邮箱:harvey.perfect@ 新浪博客地址:/harveyperfect ,有关于减肥和学习英语相关的博文,欢迎交流 把linux内存管 ...

  9. JavaScript内存管理

    JavaScript内存机制 底层语言,如C,有底层内存管理函数malloc()和free():JavaScript创建的对象不再使用的时候将会自动释放的过程称为垃圾回收,这种回收机制带来了一些问题: ...

  10. LiteOS 内存管理

    参考: [野火]物联网操作系统 LiteOS 开发实战指南 Huawei LiteOS | 中文网 8. 内存管理 8.1 基本概念 8.1.1 概念 LiteOS操作系统将内核与内存管理分开实现,操 ...

最新文章

  1. Mac 下安装 ruby 环境解决 brew 安装 yarn 问题
  2. (译)KVO的内部实现
  3. Linux拷贝排除一个或多个目录的实现方法
  4. android中跨进程通讯的4种方式
  5. ORACLE TEXT DATASTORE PREFERENCE(三)
  6. 在visual studio 2010+中调用ffmpeg编译时 报错error LNK xxxx: 模块对于 SAFESEH 映像是不安全的。...
  7. web.config中httpRunTime的属性
  8. 万能高品质PSD分层促销海报,电商美工必备
  9. Java学习系列(十四)Java面向对象之细谈线程、线程通信(上)
  10. 悟透delphi 第五章 包
  11. windows系统mysql常用命令_Windows下 MySQL命令 常用操作
  12. Python编程之求字符串长度
  13. 嵌入式Linux开发环境搭建-4-嵌入式编程基础知识
  14. Pytorch基于卷积神经网络的猫狗识别
  15. 微信小程序 基础 - 19 (登录后用户头像的更新)
  16. 素数筛线性筛详细详解(个人总结思路超长版)
  17. RuntimeError: CUDA out of memory. Tried to allocate 模型训练 GPU 显存不够报错总结
  18. h5 加载完成_从零到一:实现通用一镜到底 H5
  19. 抖音康辉机器人_抖音讯飞智声AI黑科技是什么 与明星互动就是这么简单
  20. 《海外社交媒体营销》一一

热门文章

  1. c语言计算圆的周长和面积用getchar,c语言求圆的周长和面积,已知圆的周长求面积...
  2. ( 数论专题 )【 斐波那契通项公式 + 等比数列求和公式 】
  3. 离散序列周期延拓 matlab
  4. ADD/ADHD 患者专注技巧有哪些?
  5. 简记 nvidia-smi
  6. 高职副教授职称评定条件
  7. 《道德经》第四十五章
  8. 使用PyTorch来进行肺癌早期检测:3、训练模型
  9. 自动驾驶全球布局(3)传统汽车厂商
  10. SUTD提出:基于不同数据模态的行为识别最新综述!381篇文献