五、C++内存管理机制 —— 分配器allocator(侯捷)
侯捷 C++八部曲笔记汇总 - - - 持续更新 ! ! !
一、C++ 面向对象高级开发
1、C++面向对象高级编程(上)
2、C++面向对象高级编程(下)
二、STL 标准库和泛型编程
1、分配器、序列式容器
2、关联式容器
3、迭代器、 算法、仿函数
4、适配器、补充
三、C++ 设计模式
四、C++ 新标准
五、C++ 内存管理机制
1、primitives
2、std::allocator
3、malloc/free
4、other allocators
5、loki::allocator
六、C++ 程序的生前和死后
五、C++内存管理机制 —— 分配器 allocator(侯捷)
- 演变过程:
- per-class allocator
- static allocator
- global allocator
- 标准库分配器 std::allocator
- VC6 的标准分配器
- BC5 的标准分配器
- GNU2.9 的标准分配器
- GNU4.9 的标准分配器
- GNU2.9 std::alloc
👉 该课程能让你:从平地到万丈高楼!—— 侯捷
👉 做到:心中自有丘壑!
💪 源码之前,了无秘密!
演变过程:
per-class allocator
- 为了减少
malloc
的次数,使用我们重载的operator new
(如下图右上角),一次挖一大块,然后当作linked list
串穿起来,什么时候用到,直接给就行: - 降低
cookie
的用量,由于一次malloc
就有两个cookie
占用8个字节,会增加浪费率; - 所以使用 内存池 ,不仅可以加快速度(速度考虑),还能降低利用率(空间考虑)。
union
: 借用同一个东西的前四个字节当作指针来使用,从而减少内存的膨胀。
delete
没有使用 free
,没有将内存还给操作系统,但不是内存泄漏(还都在自己的手上)。
如果要还的话,技术难点会非常高,这件事后面再讨论。
static allocator
如果还是像上面那样,任何需要分配器的
class
都需要给它写一个版本,重复性动作太多了!!!
从软件工程的角度考虑,同样的东西应该把它集中到一个地方上去,这样将来如果改变也很方便,改一个地方就行了。
在面向对象领域我们不喜欢全局的东西,所以可以将上面的动作operator new / delete
抽取出来,放到一个class
里面,这个class
就叫做allocator
。
可以将allocator
想象成一个指针,指向一个链表;意思是:每个class
里面都有专门为自己服务的小型的static
(静态的)allocator
,这个allocator
里面有一个单向链表:
- 这比先前的设计干净多了,
application classes
不再与内存分配细节纠缠不清,所有相关细节都让allocator
去操心,我们的工作是让application classes
正确工作。
还有一种偷懒的实现方式,就是把上图黄色的这些东西定义成宏!
global allocator
而标准库的allocator
如下,它有16个自由链表:
标准库分配器 std::allocator
不同的编译器所附带的标准库里头的分配器做法可能都不一样,以下是当时三种主流编译器里面所带的分配器!
VC6 的标准分配器
可以对比最新的Visual Studio !
-
VC6 的
allocator
只是以::operator new
和::operator delete
完成allocate()
和deallocate()
,没有任何特殊设计。 -
如下图右上角,容器的第二个模板参数都是
allocator
。 -
分配是以对象元素为单位。
BC5 的标准分配器
- 和VC6相同,没有任何特殊设计。
GNU2.9 的标准分配器
使用的是alloc
,alloc
是一个class
:
- 使用里面的静态函数
alloc::allocate
和alloc::deallocate
; - 512个字节,没有元素单元!
GNU4.9 的标准分配器
同样,没有任何特殊设计,和VC6,BC5一样。这个文件中已经说明,这个分配器没有用,容器的分配器不使用它!
GNU2.9 std::alloc
为什么要把他放在最后讲?因为侯老师认为它设计得最好。它和GNU4.9中的 __pool_allocator
相同。
设计是内存池的思想。
16
个指针指向16
条链表 【只服务大小为8 ~ 128字节的元素,超过128字节就调用malloc
,就不归这个pool管理了】,每条链表分配不同大小的内存空间,从左往右每一条链表间隔8
个字节。- 理论上是这样的,但是在代码实现的时候,是首先申请一个大的内存块,然后在大的内存块上进行切分,所以不同链表之间会有连线。
- 每个链表会申请
8
∗
n
∗
20
∗
2
8*n*20*2
8∗n∗20∗2 字节的内存空间,
n
代表第n
条链表 【也就是每一个块的大小为 8 ∗ n 8 * n 8∗n 个字节】 ,20
表示将内存最多切分成20
个子内存块 【根据这个设计团队的经验设定的】 ,2
表示会申请2
倍大小的内存用于战备池。这些内存空间是cookie free
的。
⭐️ embedded pointers
- 嵌入式指针:借用人家的前四个字节,当作指针。
- 下图的
obj
就相当于指针。
伴我清酒᭄ꦿ: Anacond Navigator进去后Jupyter用不了是啥情况呀,各位
weixin_43183563: 镜像源地址失效解决方法: 首先删掉旧镜像源:conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/tensorflow/linux/cpu/ 然后更换pip源:pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
2301_79795444: 还有一种可能就是冒号:记得要用英文输入法,中文的不行,你冒号后面会隔一段空白就是中文输入法输入的
11.17208: 你好,我的问题和你的也一样也是出现不是内部命令,请问你解决了吗
2301_76962792: 总结的真好 谢谢☺️