侯捷 C++内存管理 第三章节 malloc/free 笔记

系列文章目录

侯捷 C++内存管理 第一章节 memory primitives 笔记
侯捷 C++内存管理 第二章节 std::allocator 笔记
侯捷 C++内存管理 第三章节 malloc/free 笔记
侯捷 C++内存管理 第四章节 loki::allocator 笔记


文章目录

  • 系列文章目录
  • 一、VC6的malloc设计
    • 1. _heap_init()和_sbh_heap_init()
    • 2. _ioinit()
    • 3. 内存管理流程


  

一、VC6的malloc设计

  这是VC6版本下的malloc程序设计,从下往上一次执行。在该版本中有SBH(small block heap)的设计,即对于小块内存会采用特殊的分配方式。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第1张图片

1. _heap_init()和_sbh_heap_init()

  _heap_init()函数首先问系统要一块初值为4096bytes的空间命名为_crtheap,接着调用_sbh_heap_init()从_crtheap获取空间创建16个HEADER,HEADER的结构如图所示。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第2张图片
侯捷 C++内存管理 第三章节 malloc/free 笔记_第3张图片

2. _ioinit()

  (1)首先会调用malloc_crt(),在普通模式下名为malloc(),在debug模式下名为 _malloc_dbg() ,会创建256bytes的空间。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第4张图片
  (2)调用 _heap_alloc_dbg() 函数,为刚刚申请的256空间加上 _CrtMemBlockHeader 的头部额外空间和 nNoMansLandSize(大小为4bytes)的尾部额外空间,先分配空间但还没有赋值。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第5张图片
然后将不同的内存块通过指针(即pBlockHeaderNextpBlockHeaderPrev)连接起来,并将对刚刚分配的额外空间进行赋值。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第6张图片
  (3)调用_heap_alloc_base()函数,先判断区块是否小于1016bytes,若小于则调用_sbh_alloc_block()。(为什么是1016bytes?因为在判断过后要加上8bytes的cookies,加起来一共1024bytes。)
侯捷 C++内存管理 第三章节 malloc/free 笔记_第7张图片
_sbh_alloc_block()将在内存块的头尾加上4bytes的cookies,并向上补齐至16的倍数。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第8张图片
  到目前为止,对于一个申请的内存,要加上36bytes的debug空间和8bytes的cookies,加完之后再向上补齐至16的倍数,将最终的空间大小记录在头尾cookies里,若该内存已经被分配出去,则额外+1bytes,用于标记该内存已经被分配。

3. 内存管理流程

  对于之前_sbh_heap_init()创建的HEADER,每一个HEADER拥有两个指针,一个指向1MB的内存空间,一个指向用于管理这1MB空间的region。
  region中的主体部分为一个tagRegion结构体和32个group,每个group内有64组指针,每个组内有两个指针,可以维护一个双向链表,维护的内存大小从16bytes到1kb,间隔为16bytes。每一个group在结构体内对应有一个64位的bit数组,64位bit正好和64条双向链表相对应,置1表示该链表下挂有内存,置0则表示该该链表为空。
  每管理1MB内存,需要花费16k的内存。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第9张图片
  将1MB的内存空间分为32份,每份32KB,由一个group进行管理。一份又被分为8个page,每个page大小为4KB。通过双向链表将8个page连接起来。可用空间为4096 - 2*4 = 4088,但此时不为16的倍数,需要向下对齐至4080,所以可用空间为4080bytes。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第10张图片
   第一次分配: 要切割出130h的空间,返还给_ioinit(),返还形式为头指针,130h / 16 - 1 = 18,理应由#18号链表进行空间分配,但#18号链表下没有内存,所以选择右边最靠近#18号链表的#63号链表下切割130h的空间,空间大小还剩余ec0。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第11张图片
   第二次分配: 要切割出240h的空间,240h / 16 - 1 = 35,理应由#35号链表进行空间分配,但#35号链表下没有内存,所以选择右边最靠近#35号链表的#63号链表下切割240h的空间,空间大小还剩余c80。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第12张图片
   第一次回收: 在分配多次空间后要回收之前分配的240h空间,240h / 16 - 1 = 35,理应由#35号链表进行空间回收,将该内存的头尾cookies最后一位改为0,即为内存回收,同时内嵌2个指针,挂到35号链表上。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第13张图片
由于现在35号链表下有内存了,所以要将35号链表对应的bit置1。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第14张图片
   回收后的分配: 要切割出b0h的空间,b0h / 16 - 1 = 10,理应由#10号链表进行空间分配,但#10号链表下没有内存,所以选择右边最靠近#10号链表的#35号链表下切割b0h的空间,空间大小还剩余190h,再将这块内存重新分配到#24号链表,并将#35号链表对应bit置0,24号链表对应bit置1。
   每次链表状态变化时都要更改对应的bit位!
侯捷 C++内存管理 第三章节 malloc/free 笔记_第15张图片
当第一个group内的空间不再足以满足内存需求时,则进入第二个group进行内存分配。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第16张图片
   内存合并: 若不进行内存合并,会导致内存变得愈发细碎,最后不足以支持大块内存申请。返回一块内存时,先对内存下方的cookies进行判断,若下方内存块为空,则和下方合并,再对内存上方cookie进行判断,若上方内存块为空,则和上方合并,合并完成后根据内存大小嵌入指针,交由对应的链表进行管理。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第17张图片
为什么要分为32个group而不是1个group?
因为只有当一个group全回收后,才会返回给系统,如果只有一个group,全回收的概率会很低。
如何判断全回收?
每一个group都有一个int,每malloc一次就+1,每free一次就-1,所以当int为0时,该group就处于全回收状态。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第18张图片
  但一个group进入全回收状态后,并不会立刻还给系统,而是由一个defer指针指向它,当有第二个全回收group出现时,将defer指向的group返回给系统,并将defer指向新的全回收group。若此时没有第二个全回收group,且第一个group也得被拿出来分配,则取消defer指针。
侯捷 C++内存管理 第三章节 malloc/free 笔记_第19张图片

你可能感兴趣的:(C++内存管理机制,c++,笔记)