【侯捷C++-----内存管理Chapter1(至G2.9std:alloc源码)】

目录

Part1概述

Part2内存分配的每一层面

Part3四个层面的基本用法

Part4基本构件之一newdelete expression

Pat7Array new

Part8 replacement new

Part9重载

Part12-per-class allocator

 Part14 Static allocator

 Part16-补充:new handler,=default ,=delete

Part17 VC6 malloc()

Part18 VC6标准分配器之实现

Part19 BC5标准分配器之实现

Part20 G2.9标准分配器之实现

 Part22 G4.9 poll alloc用例

 part23 G2.9的标准分配器

Part24-std_malloc运行一瞥

Part27-G2.9 std::alloc源码剖析


Part1概述

*malloc是memory allocation的意思~~

Part2内存分配的每一层面

在初期,内存技术不行,因此对内存锱铢必较!

当想要申请内存时:

 最高阶的是使用STL中的内存分配器,它实际上是通过new,new[],new()这些较为低阶的函数实现的。但终归会回到malloc和free这两种函数上来。最低阶的是操作系统的内存分配函数

对内存分配的工具概览:

Part3四个层面的基本用法

对于第四种方式还不是很理解: 

 

 

Part4基本构件之一newdelete expression

new:

        1.申请一段指定类大小的空间(new->operator new->malloc)

        2.转化为对应类类型

        3.调用类构造函数

在operator new的源码中,有个std::nothrow_t& _THROW0()参数,表示这个函数不抛异常,取而代之的是返回一个空指针,用户通过判断是否为空指针来判断是否分配成功。

delete:

        1.调用析构函数

        2.释放内存   delete->operator delete->free

Pat7Array new

cookie中记录着分配空间的长度,以便free知道释放多少 

array new是分配一个对象数组,通常容易犯得一个错误是在delete的时候忘记在delete后面加[]导致内存泄漏的问题。正如上图所说的,对于类中没有指针的类,不加[]可能问题不大,因为没有指针的类析构函数本来也就没有什么大的作用;但是,如果有指针,忘记写[],那么delete只会触发一次析构函数,delete掉一个指针指向的内存,其他指针指向的内存就会泄露(不是array本身发生泄露)。如上图的psa析构,str2和str3指向的地址会发生内存泄漏(析构的顺序依编译器而定)。
 

下面两张图分别表示有指针和没指针的对象数组分配内存的区别

 delete pi并不是必要的

但如果创建的是对象的话(demo是有3个int的对象)

 对于分配一个对象数组,他会把数组的大小也放到存放对象数组的内存块的开头,free时就会看到这个3,知道要调用3次析构函数。如果在delete内存的时候不加[ ],编译器会把他当成一个无指针的对象来析构,那么,他碰到存放数组大小的那块内存的时候,布局就乱掉了

Part8 replacement new

用array new调用的是类的默认构造函数,还需要对数组中的对象进行真正的构造。

这就需要replacement new来允许我们将对象分配在已经构建的内存中

它不会进行内存分配,而是调用重载的operator new,用于返回已经分配好的内存,转型、调用构造函数

 (buf指针正是代表那块allocated memory)

replacement new 等同于执行构造函数

Part9重载

本节课所讲的内容就是讨论怎么把路线2转为路线1

 全局版本的重载:(但是影响会很大)

 更常用的是在类中进行重载:

 重载这些函数的目的是什么?——为了接管内存分配的工作

接管了它很有用!例如,可以做一个内存池

如果写了这些,就会绕过我们自己重载的函数,调用global version

 

Part12-per-class allocator

 ↑这就是一个小型的内存池(不过只能在这个类中使用)

让我们来使用一下这个写好的类:

 在刚刚的例子中,多了一根next指针,现在我们来处理一下这个问题

 

(这时还是有一个遗憾,没有把这些分配的空间还给操作系统) 

 Part14 Static allocator

前面写了next版本和优化掉next的版本,现在是第三个版本——怎么避免给每个需要内存管理的类都写一个差不多的重载呢?

 

运行查看:因为我们的chunk=5,所以每次是一下分配5块内存 

 因此每5个是连续的,其他一般不会是连在一起滴

 还有一种实现方式,将黄色的内容定义成宏(技术思想上并没有改进,只是一个偷懒工作)

 

 对于3,4版本,已经有了标准库的雏形

 Part16-补充:new handler,=default ,=delete

还记得之前operator new 里如果没有成功分配内存的callnewh()函数吗,ta就会调用上面的东西

使用方法: 

 当operator new没有能力为你分配出你所申请的memory,会抛出一个bad_alloc的异常。抛出异常之前会先(不止一次)调用一个可由用户指定的handler

 _______________________________________

=default     使用默认版本

=delete   这个成员函数不能再被调用

——————————自此进入C++内存管理第二讲标准库allocator—————————

Part17 VC6 malloc()

ch代表16进制(想申请12个字节)

 pad是为了将其填补到16的倍数

free时有一些机制,能把这一块还回去

Part18 VC6标准分配器之实现

(VC6所附的标准库中的allocator)

 在右侧可以看到,几种容器的第二个参数都是ta!

Part19 BC5标准分配器之实现

Part20 G2.9标准分配器之实现

同样,没有任何特殊设计,和VC6,BC5一样。这个文件中已经说明,这个分配器没有用,容器的分配器不使用它!

 

 Part22 G4.9 poll alloc用例

 

 我们发现这个_gnu_cxx::pool_alloc真的去掉了cookie!

 part23 G2.9的标准分配器

把ta放在最后讲是因为老师认为ta设计的最好!

ta和GNU4.9中的_pool_allocator相同

 设计是内存池的思想。16个指针指向16条链表,每条链表负责分配不同大小的内存空间

从左往右每一条链表间隔8字节。理论上是这样。不过在代码实现中,是首先申请一个大的内存块,然后在大的内存块里进行切分(所以才出现了图中32,64字节空间在一块的现象)

每个链表会申请 大小*20*2字节的内存空间,最后这个2表示会申请2倍大小的内存用于战备。这些空间都没有cookie(cookie free)

如果申请的内存空间不在这个范围内,就不归这个来管理了

源码中的deallocate没有free操作(这可能是G4.9把ta替换回去的原因?)

 <嵌入式指针>

 在工业级的应用中,对象也确实都>=4

Part24-std_malloc运行一瞥

 (RoundUp是一个追加量,计算方式是把目前的累积申请量除以16)

 (先查pool中还有没有空间)

 

 

 我们发现蓝色的战备池位置变来变去,实际上,有两根指针,他们之间就是标识的战备池

 

 

 

 

 

 我们看到,系统中明明还有很多空间...

 在了解这个过程之后,我们就可以去学习源码剖析~~

Part27-G2.9 std::alloc源码剖析

 

接下来,谈谈refill

 拿一大块内存的工作交给chunk_alloc()函数

本函数代码如下: 

  

中间失败的代码是把临近链表的空间试图给要求分配空间的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值