Stl源码剖析读书笔记之Alloc细节

时间:2024-04-28 14:34:36

阅读基础:

  Foo *pf = new Foo; 执行了两个步骤: 1)::operator new 向系统申请内存. 2) 调用Foo::Foo()构造函数构造实例.  ==> 申请内存,构造实例。

delete pf; delete; 执行了两个步骤: 1)调用Foo::~Foo()析构函数. 2). ::operator delete释放内存.         ==> 析构实例,释放内存。

Stl Alloc实现:

Stl为了高效利用内存,将这两部分分开,分成了四个操作( 构造::construct()/析构::destroy(),申请内存(alloc:allocate())/释放内存(alloc::deallocate() ).

  而设计的精髓是双层级内存分配器,主要是想实现一个内存池实现内存快速分配以及解决小型内存块频繁申请释放,耗费系统内存问题(free_list),第一级直接使用malloc和free,第二级视情况采用不同策略。

双层级内存分配器:

  1. 第一级配置器以malloc、free、realloc等C函数执行实际的内存配置、释放、重配置等操作,并实现出类似C++ new-handler的机制,它不能直接运用C++ new-handler的机制,因为它并非使用::operator new来配置内存。   所谓 c++ new-handeler机制是:你可以要求系统在内存配置需求无法被满足时,调用一个你所指定的函数。换句话说,一旦::operator new无法申请到内存时,在抛出std::bad_alloc异常状态之前,会先调用由用户指定的处理函数,该处理函数通常即被称为new-handler。

2. SGI第二级配置器的做法:如果要申请的内存大小超过128bytes时,就交给第一级配置器处理,当申请的内存小于128bytes时,则以内存池管理,此法又称为次层配置:每次向系统申请一大块内存,并维护一个*链表(free_list),下次如果有相同大小的内存需求,就直接从*链表中取出即可,如果释放小内存,就由配置器回收到*链表中(只析构实例,但回收其占用的内存)。SGI第二级配置器会将任何小内存的申请需求量上调至8的倍数,并维护16个free-lists,每个Node管理大小分别为8,16,32,,,128的多个内存块。  以为申请的内存是8的倍数,而回收的内存也必然是8的倍数,只需放到合适的free_list节点即可。

记住几个点:

   1. 内存池通过start_free, end_free,去记录当前备用的空闲内存块(必然是一整块连续的空间), end_free - start_free的结果必然是8的倍数.

2. free_list不足以分配内存时,就会向内存池申请,重新申请内存时,start_free和end_free会更新,直到重新向系统申请一块新的内存。

3. 从内存池中获取的内存不足时,如果依然有一些零头内存(必然是8的倍数),将这些内存挂载到合适的free_list节点下,再向系统申请内存。

    所有容器的内存申请和释放都是靠这个Alloc在背后完成。