侯捷老师 C++内存管理-第五讲 学习笔记

第五讲 The Other

前面我们看了关于VC6的分配器,loki的分配器以及CRT,下面我们来看看GNU C++ 对于Allocator的描述

GNU除了标准的分配器之外,还提供了更多的分配器,我们来看看这些分配器

GNU C++ 对 Allocator的描述

侯捷老师 C++内存管理-第五讲 学习笔记_第1张图片

我们先来看它的官方文档说明,

当将元素加入容器中,容器必须分配更多的内存来保存这些元素,于是向模板参数Allocator提出申请。并且string也是一种正规的容器。而模板参数的默认调用的是allocator标准库的分配器,而我们知道它没有任何特殊的设计而是调用 ::operator new,与 ::operator delete。但是在分配内存的时候何时调用以及调用的频率没有指定,所以说我们可以自己来设计

侯捷老师 C++内存管理-第五讲 学习笔记_第2张图片

其中最容易满足的是就是每当容器需要内存就调用一次 ::operator new,每当容器释放内存就调用一次 ::operator delete,并且这种做法与前面我们学到的内存池做法相比,速度会慢,但是可以在极大范围的硬件和操作系统上有效运作。

我们先来看其中的2种allocator,看它的实现发现,它其实就是这种最容易满足的实现,没有内存池的设计,但是第一种调用的是operator new 是可以被重载的

侯捷老师 C++内存管理-第五讲 学习笔记_第3张图片

还有一种做法是智能型allocator,将分配的内存加以缓存,就是我们所说的内存池设计,

其中上面 fixed-size-pooling cache,就是前面第二讲中我们学到的G2.9 std::alloc ,有一个链表数组,每一个数组元素管理不同固定大小的内存;还有一种较复杂的bitmap index实现;

__ gnu_cxx::pool_allocator就是 fixed-size-pooling cache的一种实现(实际为: pool_alloc)

__gnu_cxx::bitmap_allocator就是bitmap index的一种实现

而 __ gnu_cxx:: __mt_alloc是关于多线程的

侯捷老师 C++内存管理-第五讲 学习笔记_第4张图片

这里没有什么特别的,阅读即可

侯捷老师 C++内存管理-第五讲 学习笔记_第5张图片

刚才我们提到了两种智能型allocator,还有另外两种智能型debug_allocator与array_allocator。

debug_allocator这个分配器其实没有什么作用;

array_allocator它是分配一块已知且大小固定的容器,所以它是用数组支撑的,是静态的。然后我们看最后一句可以在program startup情况下使用,那么startup是什么时候呢,来看右边的call stack,在调用main()函数前的时候就是startup。 但我们知道在进入_heap_init()后第一个动作就是做内存管理的初始化,所以说这个分配器的用途也很小

array_allocator

上面出现的7种allocator,有些我们前面已经学过,这里就不介绍了

而debug_allocator没有什么用,所以看上面文字即可,

mt_allocator是关于多线程的,这里也不多说了,

我们先来看看上面出现的array_allocator

侯捷老师 C++内存管理-第五讲 学习笔记_第6张图片

我们可以看到,它是管理了一个数组,是静态的,所以它的一个特别之处就是不需要释放内存

我们主要来学习bitmap_allocator的行为模式

bitmap_allocator

侯捷老师 C++内存管理-第五讲 学习笔记_第7张图片

其中最重要的是_M_allocate_single_object()与 _M_deallocate_single_object,根据命名我们可以看出来它是只分配释放一个,再来看,if判断如果n == 1的话就调用它,否则就调用operator new与operator delete,所以可以看出它只为那种一次只分配一个元素的用户服务,那它为什么这么设计呢,我们知道分配器是为容器服务的,而容器其实是一次只要一个元素的,所以这样设计是非常合理的。

侯捷老师 C++内存管理-第五讲 学习笔记_第8张图片

我们先来介绍bitmap中出现的一些变量,

blocks,图中的一格就是一个block,大小是8的倍数

super-blocks,图中的一整串就是一个super-blocks,申请的64blocks加上2个bitmap,加上前面1格use count

bitmap,它使用的单元是int,也就是4个字节,32bit,现在有64个block所以需要64个bit,也就是2个bitmap,其中每一个bit的作用是记录申请block的状态,已经给出(0)或自己管理(1)

再来看bitmap的前面一格,use count,它是记录的是当前已经使用的block块数,现在是0

第一格,记录的是它后面super-block的大小,记录后不会改变,所以说它不属于super-blocks,现在假设你要分配1000个元素,那么就会有很多super-block并且它的大小会越来越大,成倍的

mini_vector,它是自己实现的vector,管理super-blocks;

现在有一个单元,它有两根指针,分别指向blocks的头尾,如果有第二个super-blocks,就会有第二个单元,指向第二个blocks的头尾;而这些单元放在mini_vector中,而mini_vector是依靠start,finish与end_of_storage管理这些单元,start指向第一个元素,finish指向最后一个元素的下一个位置,end_of_storage指向最后一个单元的后一个位置

侯捷老师 C++内存管理-第五讲 学习笔记_第9张图片

现在假设分配了1个block,注意观察图中一些变量的变化,

然后假设分配了63个block,继续观察变量的变化,这里是为了帮助理解它的设计

侯捷老师 C++内存管理-第五讲 学习笔记_第10张图片

可以发现bitmap的状态与blocks是反着对应的,bitmap的最左侧对应blocks的最右侧

如果归还一次,就可以更好理解了

侯捷老师 C++内存管理-第五讲 学习笔记_第11张图片

侯捷老师 C++内存管理-第五讲 学习笔记_第12张图片

那如果我们将第一个super-blocks用光呢,

我们看到,第一个的bitmap全部为0表示全部给出,64表示给出64个block,并且第一块记录super-blocks大小524bytes不会改变,那么现在需要第二个super-blocks,分配了128个blocks,使用了4个bitmap来记录,总共大小为1044bytes。

然后可以看到,mini_vertor进行了扩容,现在有了2个单元来管理2个super-blocks,所以finish要更改指向为最后一个元素的下一个位置,对比上方来更好理解;

再来看 growed up,成长,什么意思呢,我们知道当vector的容器不够时,它会扩容,1.5 / 2(这里是2)倍的扩容,而扩容就会带来移动,就是说如果上一次分配的内存后面不够扩容时,它就会移到另一个可以满足扩容的位置。

如果我们将第二个super-blocks也用光呢,

侯捷老师 C++内存管理-第五讲 学习笔记_第13张图片

那么就有第三个super-blocks,而mini_vector也要继续扩容2倍,有了4个单元,finish的指向改为最后一个元素的后面,而第4个单元暂时没用。

再来看右边一段话,它的设计是,如果一个super-blocks没有全回收的话继续分配就会成倍的分配,而如果全回收的话,下一次分配就会减半。这是它的设计方式,也许是经验考量。

再来看左侧,它说vector的单元entries没有个数限制,跟标准库的类似,这是合理的;每一个单元代表一个元素类型,且不会和其他类型混用,即使相同大小。

侯捷老师 C++内存管理-第五讲 学习笔记_第14张图片

我们来看看什么是全回收,

1st super-blocks的全回收,将申请分配的blocks全部归还,然后改变bitmap的值全为1,前面一块改为0,这样就是全回收。然后allocator使用了另外一个名为free_list的mini_vector,用一根指针来管理,但是最多只能有64根,超过便归还给OS,下一次再分配的时候,就分配本次的一半128blocks。

现在衍生出两个问题,

问:如果 #0 super-blocks回收2blocks,而未全回收,然后再分配2blocks,那么会从#0分配,还是#2分配呢?

答案是,后者;这里是它的设计方式

再问:如果,将#2用光,然后再分配2blocks,那么它是会从#0分配,还是在新建#3呢?

答案是,前者,这里也是它的设计方式

侯捷老师 C++内存管理-第五讲 学习笔记_第15张图片

现在将#1,第二个super-block也全回收,交给free_list来管理,而free_list的元素是以super-block的大小来排列的,假设现在它有了64个指针,也就是说再来一个全回收就要归还,它是归还这65个super-block中占用内存最大的。因为它是按照大小来排列的所以,它会比较最后一个跟新来的,free掉最大的。

侯捷老师 C++内存管理-第五讲 学习笔记_第16张图片

设现在它有了64个指针,也就是说再来一个全回收就要归还,它是归还这65个super-block中占用内存最大的。因为它是按照大小来排列的所以,它会比较最后一个跟新来的,free掉最大的。

侯捷老师 C++内存管理-第五讲 学习笔记_第17张图片

如果,现在分配的3个super-block全部回收,名为mem_blocks的mini_vector的大小并不会变,而是有4个空的单元,假设又继续分配1个block,那么从free_block中取出一个交给mem_blocks。

最后:
欢迎指正不足或错误的地方。如果文章对你有所帮助,欢迎点赞支持。欢迎转载。

你可能感兴趣的:(c++,学习,开发语言)