文章目录

  • 1. SBH 概观
  • 2. SBH 运作流程
    • 2.1 _heap_init() 和 __sbh_heap_init()
    • 2.2 _ioinit()
      • 2.2.1 _malloc_dbg()阶段
      • 2.2.2 _heap_alloc_dbg()阶段
      • 2.2.3 _heap_alloc_base() 阶段
    • 2.3 VC6 内存管理的分配和释放
  • 3. SBH 实现细节

malloc 和 free 作为 c 语言中的内存申请函数(new是运算符),malloc 实际上依据申请的空间大小,大空间调用os API(HeapAlloc()),小空间使用SBH(小于等于1K)。



1. SBH 概观

SBH 是 Small Block Heap 的缩写,可见于 VC6 的源码当中,而迭代到 VC10 及之后版本,SBH机制虽继续存在,但已经整合至操作系统 API 当中去了(Windows Heap)。以下内容均以 VC6 版本为例进行展开。

SBH 顾名思义,即小内存区块栈。在实际使用中,若是请求的内存空间 size > threshold 则使用 HeapAlloc() 从 _crtheap 当中取用内存,若 size < threshold(_sbh_threshold = 1016 = 1024-8,SBH负责1KB及以下大小内存区块的分配请求,同时1KB内存大小又有固定的两个4Bytes的cookie开销,所以实际大小为1016Bytes)则从 SBH 当中取(实际区块来自于 VirtualAlloc(),也是os API,先分配虚拟地址池)。

了解 SBH 的整个运作流程,需对流程过程有个大致的概念,概念最好的建立方法是看 SBH 运行时的系统调用栈(Call Stack),见下图:



2. SBH 运作流程

对于整个 SBH 进行小于 1KB(1016Bytes) 的内存分配的流程结合系统调用栈总结如下:


2.1 _heap_init() 和 __sbh_heap_init()

作为 SBH 的开始阶段所调用的两个函数。由 __cdel_heap_init() 创建/申请出一个大的内存区块 _crtheap;若是申请内存成功,调用 __cdecl_sbh_heap_init() 在其上配置/创建 SBH 运作所需的 HEADERs, REGIONs 等控制/组织信息做管理之用。

可以由上图看到,其中的 BITVEC 是 unsigned int 类型的别名。对于 bitEntryHi 和 bitEntryLo 则均为32的空间,使用拼接的概念,高32位和低32位(不是很清楚作用,设想是存储下一个Header地址?针对64bit系统的话,不过Header大小固定的话若是连续区间也无需这么设定?);还一个 bitvCommit 字段,是为了记录相应序号的 Group 是否已经 VirtualAlloc(addr, 32KB, MEM_COMMIT,…) 过,即是否已经实际分配了内存。


2.2 _ioinit()

在经过 2.1 中步骤后,SBH 的管理内容所包含的 16个HEADERs 的建置即告一段落,接下来即是对 分配内存请求 进行处理的过程了。


2.2.1 _malloc_dbg()阶段

依据调用栈的调用顺序,在依次调用 _heap_init() 和 __sbh_heap_init() 并逆序返回后,应当调用 _ioinit() 函数了,遵照首次分配为 dbg 模式的设定?,首先调用 _malloc_dbg()(在 #ifndef _DEBUG 无效/未命中的情况下)


2.2.2 _heap_alloc_dbg()阶段

首先了解 SBH 对所分配的内存区块的结构设计/约定是怎样的。可分为三部分 ①_CrtMemBlockHeader;②data[nDataSize];③anotherGap[nNoMansLandSize],由此也可看到最终所分配出去的 data[nDataSize] 的数据空间实际所耗用的内存空间大小 blockSize = sizeof(_CtrMemBlockHeader) + nSize + nNoMansLandSize,如下图所示:

_heap_alloc_dbg() 图(1) _heap_alloc_dbg(),其中链表指的是双向环形链表 图(2)

图(1)中的 nBlockUse 字段表示的是当前内存块的类型,一般是 _NORMAL_BLOCK 和 _CRT_BLOCK(系统使用)两种类型?见_malloc_dbg()阶段的配图;图(2) 对应填充的意思是对内存空间进行抹除/格式化,而不是初始化。


2.2.3 _heap_alloc_base() 阶段

以上阶段计算出了为处理该次内存请求所需的实际内存空间大小并对相应大小的内存空间进行抹除或格式化。

那么该阶段负责所分配内存空间 实际的内存分配组织信息的建置更新

  • 内存分配方案的选用 & __sbh_alloc_block()
//若是所需实际内存大小小于1K,减去cookie大小4*2,使用SBH进行分配
if (size <= _sbh_threshold) { //_sbh_threshold = 3F8, i.e. 1016 //1024 - 8 = 1016,减去两个4bytes的cookiepvReturn = __sbh_alloc_block(size); if (pvReturn) return pvReturn;
}
//若是大于1K,则使用HeapAlloc()这一os API进行内存分配
if (size = 0) size = 1;
size = (size+ ...) & ~(...);  //将size调整为8的倍数?
return HeapAlloc(_crtheap, 0, size);

接下来假设所分配的内存大小小于1016bytes,即使用SBH进行内存块的分配。所以接下来依据调用栈,将调用 __sbh_alloc_block() 函数,其间由于无法保证 size 是16的倍数,所以类似于std::alloc中的ROUNDUP()行为,将其大小向上调整为16的倍数。

// add 8 bytes entry overhead and round up to next para size
sizeEntry = (intSize + 2 * sizeof(int)+ (BYTES_PER_PARA - 1))& ~(BYTES_PER_PARA - 1);

在操作系统中一般将 16 倍数大小的内存空间称为一个 PARA,4 倍数大小的内存空间成为一个 PAGE;

  • __sbh_alloc_new_region()

SBH中的 REGION 的结构包含四个字段,如下图所示:

  • __sbh_alloc_new_group()

观察以下两图,可以看到group的具体结构,每个group可支配和管理的内存大小为32KB,而后又分为8个page,每个page是4KB,这也是为何计算机分配内存的最小单位为4KB的原因。在看时可结合上图中的group的具体实现结构进行理解。

group的结构图 page,listHead[]细节说明

经由以上步骤,SBH 已经将一定数量的内存空间纳入自己的管理之下,并能够依照自己的规则将这些内存分配给客户以满足客户所需的请求。


2.3 VC6 内存管理的分配和释放

由ioinit.c,line#81申请大小为100h的内存空间,通过加上head和tail等控制信息后大小为130h,因为是第一次分配,所以从#63链表进行分配,实际上应当由#18链表进行分配(130h=若该序号的链表上有空闲空间的话,130h=304,304/16-1=18)。

因为是第一次分配,之前所进行的虚拟地址分配(MEM_RESERVE)就需要进行实际分配物理地址了(MEM_COMMIT)了(注意到每次申请1MB的内存空间),分配完毕并将内存交给客户之前,还须把对应的group当中的cntEntries字段+1。同时给交给客户的内存空间两端建立区隔,区隔当中的数值应当是该块内存数据空间大小+1

若是经过了14轮内存分配之后,第15次的操作为内存释放,此时所归还的内存空间仍从属于#0 group管理,归还的空间大小为240h(240h=576, 576/16-1=35),即应当归还至#35序号的链表,图片中的序列共16位,表示的是16进制下的64位数据。可以看到归还后的数据应当为(0x 0000 0000 1000 0001,即第36位应该更改状态为1,表示第36个链表有可支配的内存空间)。

经由以上步骤,第16次操作是申请190h大小的内存空间(190h=400,400/16-1=24),所以应当由#24list来提供内存空间,但是此时#24list并没有可供分配的空间,由15次操作可知更大空间链表中#35list有空间,所以从240h中划出190h供客户使用,并建立新的数据隔离区隔。下面图片有点问题,划分后的b0大小的区块应当是可以分配的,类似std::alloc作为碎片整理到对应的链表当中去。



3. SBH 实现细节

  • 释放区块后对连续空闲区块合并
    为了尽量减小内存空间的碎片化,将连续空闲的区块进行合并是SBH进行碎片管理的思路。分为向上合并和向下合并。示意图如上所示。
  • free(p)时如何确定p所处的Header,Group,free-list内
    思路:由p计算出其处于哪个Header所管理的内存地址范围,再进一步根据p的确切值得到Group号(指针减掉头大小再除以32KB),最后计算得到free-list的序号(长度除以16bytes);
  • 由上介绍可以看到SBH是分段式对内存进行管理的,其显而易见的好处是可以判断内存是否可以进行全回收,即交还给OS,靠的就是Group当中的cntEntries字段,若是为0,则可进行全回收;
  • 内存归还的延迟机制,正如概述图片中所讲,为了内存分配的高效性,对内存的回收采用了Defering的延迟机制,若是有新的内存请求,则先从Defering group当中分配满足需求,若是有新的回收请求,则对上次的回收空间进行真正的回收,将该次回收请求的空间设为Defering group;

malloc / free:SBH(Small Block Heap)——以VC6为例相关推荐

  1. opatch java.lang.OutOfMemoryError:Java heap space错误一例

    一套11.1.0.6的RAC系统在在使用opatch工具时出现了OutOfMemoryError: Java heap space错误,其错误日志如下: opatch lsinventory Invo ...

  2. 从源码角度剖析VC6下的内存分配与切割的运作

    目录 前言 1.heap初始化 2.第一次分配内存,计算真正区块大小 3.new_region管理中心 4.__sbh_alloc_new_group()切割第一次分配好的内存 5.开始切割内存 前言 ...

  3. 【C++生前死后】生前做了什么

    目录 1.StartUpCode在哪? 2.VC6.0的调用栈 2.0总览 2.1_heap_init()  2.2 ioinit() 1.StartUpCode在哪? 以VC6.0为例. 2.VC6 ...

  4. 软件调试之堆和堆检查

    当用户启动一个程序时,系统会将程序文件从外部存储器(硬盘等)加载到内存中.当程序工作时,需要使用内存空间来放置代码和数据.在使用一段内存之前,程序需要以某种方式(API或库函数)发出申请,接受到申请的 ...

  5. C++内存管理(4):malloc的秘密

    这次我们深入了解malloc,malloc究竟分配多么大的空间,以及整个过程是如何进行的. malloc其实并没有想象中那么低效,在底层的设计中也是充满着精妙之处的. 以下分解内容以VC6版本为基础. ...

  6. malloc()/free()的实现

    malloc()/free()的实现 目录 brk()/sbrk() C 风格的内存分配程序 一个简单的分配程序 参考 malloc()/free()是C语言标准库中的内存分配函数. C标准库中与内存 ...

  7. 溢出科普:heap overflow溢出保护和绕过

    pr0mise · 2016/04/11 9:50 0x00 第一部分:heap overflow 接上文,来看另外一种溢出方式:堆溢出.相对于栈溢出来说,稍微麻烦一点 本文算是一个笔记,技术有限,难 ...

  8. malloc分配内存的实现中brk和mmap的区别

    malloc函数族: #include <stdlib.h>void *malloc(size_t size);void free(void *ptr);void *calloc(size ...

  9. malloc的内存分配原理

    0 堆内存的在计算机内存中的形式 根据<The C Programming language>推测得到堆内存,图中的Heap区域即为堆内存块(Heap区域的数目不代表计算机堆内存的真实数目 ...

最新文章

  1. 拨测工具_您可以拨多少钱? 快速简单地介绍有用的工具。
  2. 东半球最接地气的短链接系统设计
  3. 人工智能高考511分,未来有望考上东京大学!
  4. PYTHON——数据存储:MySQL数据库
  5. Android热修复技术原理详解(最新最全版本)
  6. Linux内核开发者大会 开始报名啦~
  7. mysql中的rm语句用法_SQLMap部分命令用法详解
  8. 基于JMP的神经网络设计案例分析
  9. 天津大学计算机系分数线,2019天津大学录取分数线及历年专业分数线统计表【文科 理科】...
  10. MMC 事件查看器无法打开
  11. Notepad++安装JsonViewer插件
  12. Qt下使用QAxObject进行Word转换为PDF出现错误 error: undefined reference to `QAxObject::QAxObject(QObject*)‘
  13. 海康设备通过SDK获取和设置设备网络参数
  14. python新年快乐代码_Python第二天 祝大家新年快乐
  15. Bug随手记----关于java.lang.IllegalStateException: The following classes could not be excluded because the
  16. 网络基本功(二十四):Wireshark抓包实例分析TCP重传
  17. SQL 注释语句 (--与/*...*/)
  18. 幼儿园案例经验迁移_多种形式培养幼儿的迁移讲述能力
  19. centos安装gcc的方法
  20. Vue3 使用 store 给请求头配上 token

热门文章

  1. 毕业生、结业生、肄业生区别
  2. html简单组件(九):简洁的商城订单列表显示(带状态)
  3. c语言列车调度 栈,算法:列车调度(Train)
  4. android 源码编译core dumped,Ubuntu18.04 编译Android 8.1 源码出现的问题及解决笔记
  5. 【生成对抗网络】基于DCGAN的二次元人物头像生成(TensorFlow2)
  6. 源码阅读:SDWebImage(十)——SDImageCacheConfig
  7. Mysql五大数据类型
  8. 微信小程序封装request请求,primise队列化,async await做同步处理,缓存token信息
  9. UE4 MotionBuilder快速动画重定向
  10. HIVE厂牌艺人_Labelwarts Vol. 2:洛杉矶天才厂牌 Odd Future Records 的开始到结束