《STL 源码剖析》之空间配置器

26 篇文章 1 订阅

首先,这个为什么叫“空间配置器”而不叫“内存配置器”呢?因为空间不一定是内存,也可以是磁盘或者其它存储介质。

下面我们来看一段代码:

	class Foo{ ... };
	Foo *p = new Foo;
	delete p;

这在C++中是很普遍的内存申请与释放操作。其中new算式内含两个阶段的操作:
(1)调用::operator new配置内存,即申请内存
(2)调用Foo::Foo()构造函数,构造对象
同样的delete算式也包含两个阶段的操作:
(1)调用Foo::~Foo()将对象析构
(2)调用::operator delete释放内存
为了精密分工,STL allocator将这两阶段操作区分开来。内存配置操作由alloc::allocate()负责,内存释放操作由alloc::deallocate()负责;对象构造由::construct()负责,对象析构操作由::destroy()负责。下面分别进行介绍。
::constuct()没有什么好说的,对于::destroy(),有两个版本,第一个版本传入指针参数,调用类本身的析构函数即可;第二个版本,接受两个迭代器,first和last,准备将该区间内所以对象都析构掉。我们并不知道这个范围有多大,如果很大,而每个对象的析构函数都无关痛痒(比如析构函数为空),那么一次次调用这些无关痛痒的析构函数,对效率是一种伤害。为了解决这个问题,底层提供了一种方法,用来判断析构函数是否为trivial,这个后面会介绍(traits)。
空间的配置与释放,哲学思想有如下四个方面:
(1)向system heap要求空间
(2)考虑多线程状态
(3)考虑内存不足时的应变措施
(4)考虑过多“小型区块”可能造成的内存碎片问题
C++的内存配置基本操作是::operator new(),内存释放基本操作是::operator delete()。其实这两个全局函数相当于C的malloc() 和 free() 函数。SGI正是以这两个函数完成内存的配置与释放。
对于上面所述第四点,即小型区块所可能造成的内存破碎问题,SGI设计了双层级配置器。第一级直接使用malloc()与free()函数。第二层级视情况不同采用不同的策略:
当配置区块超过128bytes时,视之为“足够大”,便调用第一级配置器;当配置区块小于128bytes时,视之为“过小”,为了降低额外负担,便采用memory pool方式处理。具体如下:
每次配置一大块内存,并维护对应的自由链表。下次若再有相同大小的内存需求,就直接从free-lists中拔出。如果客端释放小额区块,就加入到free-lists中。同时为了方便管理,会将任何小额区块的内存需求量上调至8的倍数。
有人可能会提到,为了维护链表,每个节点需要额外的指针(即指向下一个节点),这又会造成额外负担。这里提供了解决方法:

	union obj{
    		union obj * free_list_link;
    		char client_data[1];
	}
上述obj所用的是union,由于union的特性,从第一个字段观之,obj可被视为一个指针,指向相同形式的另一个obj。从其第二字段观之,obj可被视为一个指针,指向实际区块。
其实这个自由链表和操作系统中内存碎片管理颇为相似,操作系统中也是有一个链表,把内存碎片排序连起来。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值