首页 > 其他分享 >STL之空间配置器

STL之空间配置器

时间:2022-12-15 21:11:19浏览次数:39  
标签:alloc Foo STL 配置 链表 内存 空间 区块

空间配置器 allocator 只是基层内存配置/释放行为的一层薄薄的包装,没有任何效率上的强化。一般而言,C++ 内存配置操作和释放操作如下:

class Foo{ ... };
Foo* pf = new Foo;	// 配置内存,然后构造对象
delete pf;		// 将对象析构,然后释放内存

其中,new 内含两阶段的操作:

  1. 调用 ::operator new 配置内存;
  2. 调用 Foo::Foo() 构造对象内容。

delete 也内含两阶段的操作:

  1. 调用 Foo::~Foo() 将对象析构;
  2. 调用 ::operator delete 释放内存。

allocator 将这两阶段的操作分开来。内存配置操作由 alloc::allocate() 负责,内存释放操作由 alloc::deallocate() 负责;对象构造操作由 ::construct() 负责,对象析构由 ::destroy() 负责。

在 <stl_construct.h> 中,destroy() 有两个版本。第一个版本接受一个指针,将指针所指之物析构掉。而第二个版本接受两个迭代器,将 [first, last) 范围内的所有对象析构掉。但是如果每个对象的析构函数都无关痛痒,而且对象数目很多,则会造成效率上的问题。所以首先利用 value_type() 获得迭代器所指对象的型别,再利用 __type_traits<T> 判断该型别的析构函数是否无关痛痒。

在 <stl_alloc.h> 中,考虑到小型区块所可能造成的内存碎片问题,SGI 设计了双层级配置器:

  1. 第一级配置器直接使用 malloc() 和 free()
  2. 第二级配置器则视情况采用不同的策略。

当配置区块超过 128 bytes 时,便调用第一级配置器;当配置区块小于 128 bytes 时,便采用复杂的内存池(memory pool)整理方式。

无论是第一级或第二级配置器,SGI 还为它再包装一个接口如下,使得配置器的接口符合 STL 规格:

template<class T. class Alloc>
class simple_alloc {
public:
    static T* allocate(size_t n)
    { return 0 == n ? 0 : (T*)Alloc::allocate(n * sizeof(T)); }
    static T* allocate(void)
    { return (T*)Alloc::allocate(sizeof(T)); }
    static void deallocate(T* p, size_t n)
    { if(0 != n) Alloc::deallocate(p, n * sizeof(T)); }
    static void deallocate(T* p)
    { Alloc::deallocate(p, sizeof(T)); }
};

第一级配置器以 malloc()、free()、realloc() 等 C 函数执行实际的内存配置、释放、重配置操作,并实现出类似 C++ new-handler 的机制。SGI 以 malloc() 而非 ::operator new 来配置内存,因此,SGI 不能直接使用 C++ 的 set_new_handler() 的操作,必须仿真一个 set_malloc_handler()。一旦内存不足,无法分配,则抛出 bad_alloc 异常,或是利用 exit(1) 直接终止程序,在此之前,会先调用由客户指定的处理例程。

对于第二级配置器,避免了太多小额区块造成内存的碎片,如下图:

SGI 第二级配置器的做法是:如果区块过大,超过 128 Bytes 时,就移交给第一级配置器处理。当区块小于 128 Bytes 时,则以内存池管理,也称为【次级配置】。每次配置一大块内存,并维护对应的自由链表,下次若有相同大小的内存需求,则直接从自由链表中取出。同时,如果客户释放小额区块,则由配置器回收到自由链表中。为了方便管理,SGI 第二级配置器会主动将任何小额区块的内存需求量上调至 8 的倍数(例如客户需求 30 Bytes,则上调到 32 Bytes),并维护 16 个链表,各自管理大小分别为 8、16、24、32、40、48、56、64、72、80、88、96、104、112、120、128 bytes 的小额区块。自由链表节点结构如下:

union obj {
    union obj* free_list_link;
    char client_data[1];
};

而结点正是因为 union 的缘故,所以从第一个字段出发,obj 被视为指向另一个结点的指针,从第二个字段出发,obj 被视为指向实际区块的指针。这样的方式节省了内存,省下了额外负担。

第二级配置器通过 allocate() 分配内存,通过 deallocate() 释放内存。如果申请内存时,自由链表中没有可用区块时,就调用 refill() 为自由链表重新填充空间。新的空间取自内存池,由 chunk_alloc() 来完成,它通过 end_free - start_free 来判断内存池的水量。

如果水量充足,就直接调出 20 个区块返回给自由链表。

如果水量不足以提供 20 个区块,但是还足够提供一个以上的区块,就取出这个不足 20 个区块的空间出去,同时,将 pass-by-reference 的 nobjs 参数修改为实际能够供应的区块数。

如果内存池连一个区块空间都无法供应,此时便需要利用 malloc() 从 heap 中配置内存,为内存池注入新的内存以供需求。先将内存池剩余的零头配置给合适的自由链表,然后向堆空间申请新的内存,新注入的水量为需求量的 2 倍,再加上一个随着配置次数增加而愈来愈大的附加量。最后将将第一块返回给用户,剩下的 19 块依附在自由链表上,余下的 20+n 个留在内存池中。

如果整个堆空间的内存已经不够申请了,malloc() 行动失败,那么 chunk_alloc() 就找寻有无尚未使用之区块,且区块足够大的自由链表,如果找到了就取出来;如果没找到就调用第一级空间配置器,利用其 out-of-memory 处理机制,或许有机会释放其他内存拿来使用。如果可以就成功,否则发出 bad_alloc 异常。

标签:alloc,Foo,STL,配置,链表,内存,空间,区块
From: https://www.cnblogs.com/tuilk/p/16986033.html

相关文章

  • mysql 主从docker-compose 配置
    version:'3'services:#主从mysql配置mysql-master:image:mysql:8.0.24restart:alwayscontainer_name:mysql-masterenvironment:M......
  • opencv在Xcode中的配置方式
    今天想把一个以前小时候玩过的游戏用UE4作成3D的,发现这个游戏每一个地图是16X14的地图块组成的,因为自已不想一张地图一张地图的去照着以前的地图拼出所有的地图数据,作为一......
  • 【Dubbo】快速入门案例以及配置详情
    文章目录​​1、Dubbo的前世今生​​​​2、Dubbo的快速入门​​​​2.1、Dubbo的基本架构​​​​2.2、Nacos​​​​2.3、管理后台​​​​2.4、入门案例​​​​2.5、代......
  • 【数字视频技术介绍】| 编码中的时间冗余和空间冗余
    公众号回复:666,领取学习资源大礼包这是一份循序渐进的视频技术的介绍,来自Github仓库:​​https://github.com/leandromoreira/digital_video_introduction​​本文档旨在尽......
  • react 配置代理
    //配置代理项目中自带(http-proxy-middleware)constproxy=require('http-proxy-middleware')module.exports=function(app){  app.use(    proxy......
  • silence 主题配置说明
    silence代码开源地址1.基础设置2.css样式注意:要禁用默认CSS@mediaonlyscreenand(max-width:1365px){.github-corner{display:none}}@mediaonlyscreenand(......
  • MySQL5.7.21开启Gtid配置主从复制
    mysql5.7.21开启Gtid配置主从复制一、环境:mysql主库master:10.0.0.101172.168.1.101mysql从库slave:10.0.0.103172.168.1.103mysql-5.7.21-linux-glibc2.12-x86_64.tar.gz......
  • Rocky Linux9.1 手动配置静态IP
    #修改网络的配置文件[root@zabbix~]#vim/etc/NetworkManager/system-connections/ens192.nmconnection[connection]id=ens192uuid=0572911c-bd45-33c3-9442-c896e0320......
  • 221215_文件系统造成“占用空间”与“大小”不同
    存储空间都去哪了?占用空间比文件大太多?可能是文件系统和默认簇大小惹的祸小平友littlePING于2019-11-3019:20:28发布9049收藏12文章标签:文件系统簇硬盘占用空......
  • binlog及审计配置
    实现方案[binlog+init-connect],binlog可以审计到所有数据记录及表结构变更相关的记录,但,不记录连接的信息,要通过额外的手段来记录,比如showfullprocesslist;是可得出......