20130331 stl源码剖析之stl分配内存设计

来源:互联网 发布:zabbix 监控域名 编辑:程序博客网 时间:2024/06/05 17:57

 

   本周买了本侯捷的《stl源码剖析》,按照前言所说按顺序阅读下去,感觉stl通篇都是范型的例子,对于C++语言本身来言,我比较不熟的地方就在于虚函数的表,还有就是末班的应用,希望读源码的时候可以丰富关于范型编程的思想。

    看完stl的空间适配器一节,感觉大师们写代码所考虑的细节,确实要缜密多了。

    1、内存分配与释放、 对象构造与析构的分开

     我们都知道,当我们new一个class对象的时候,如下面的例子

     class Foo {...};

     Foo *p_Foo = new Foo();

   new的表达式中包含两个阶段:(1)调用::operator new 配置内存  (2) 调用Foo的构造函数

   对于我们来说,这是个正常的现象,甚至与没有办法去改变,stl 源码在这里的处理是:为了精密分工,STL allocate决定将这两个阶段操作分开,内存配置和释放有alloc来完成,对象的构造和析构由::construct() 和 ::destory()负责。 简单来讲就是 内存分配 和对象构造 将不会再一个表达式中完成,这里就需要使用一个新的函数, placement new

 

(1)placement new

      这个是STL ::construct() 使用的函数。 placement new  我会另开一篇博客来讲解,这里先来看下 new 与placement new 的区别

     上面已经说了,new 操作有两个步骤,而placement new  则既不分配内存,也不调用其构造函数,它只是指向已经分配好的内存中的某段内存中的指针,这个函数就可以实现stl的想法,把内存分配与释放、 对象构造与析构的分开。

     这里顺带说一下STL 析构的一个技巧,析构函数分为trivial 与non-trivial,stl析构函数的参数是一系列的相同类型的对象,万一这个对象很大,且每个析构函数都是无关痛痒的(所谓trivail destruct) ,那么一次次的调用这样的析构函数对效率是一种伤害,所有STL会先判断下这个析构函数的类型,额、、但是C++貌似没有这样的函数,我们接下来看STL是怎样实现它的。

(2)STL 的内存池的设计

  STL内存配置设计有以下的特点:

   像system heap 要求空间、考虑多线程状态、考虑内存不足的应变措施、考虑过多 小型区域 可能造成的内存碎片问题。

   STL设计了两种分配方式,当需要分配的内存大于一个数值时(STL中为128bytes),就直接使用malloc来分配,一旦失败就做一个相应的处理,这个相应的处理可以由客户端来自己设计。

    对于第二种分配,当数据小于128bytes时,内存池来分配内存。内存池是这样设计的,主动分配并维护16个freelist(可以看成一个链表),按照8的倍数依次生成16个节点,每次要申请内存的时候,先把要申请的内存的数值换成与8的倍数最靠近的一个节点,然后去该节点下去找是否有足够的空间,如果有直接返回,如果没有的话,先从内存池看是否有至少一个空间。举个例子,如果你申请一个大小为24的空间,而24这个节点刚好没有空间,但是12的节点有两个,内存池就会先把这2个12个空间拿出来返回给客户端,这样就是一个减少碎片空间的例子。也就是说一旦该节点没有内存可以分配了,但是内存池中还有剩余的空间,则将其挂到freelist中去,然后重新分配内次。对于stl来说,释放空间是需要将指针和大小作为参数传过来的。

    见过另外一种内存分配机制。它讲空间的大小都是按1024的倍数分成若干份,若用户申请的内存小于1024的倍数,则将其不齐,当内存池不够用的时候,它会先从最大的内存块开始释放,自动释放,为了实现自动释放,它定义了另外一个set map,用来存储所有的分配过得内存,然后从大到小的释放,直到释放的空间足够。


  其实这里介绍内存分配的话,如果配上图就更好说明了,可惜图画的不好,以后会专门去学一下画图,如果有不明白的地方或者错误的地方可以发邮件到我邮箱,plyj0123@163.com

希望跟朋友一起分享一起进步


 

  

原创粉丝点击