STL提供了很多泛型容器,如vector,list和map。程序员在使用这些容器时只需关心何时往容器内塞对象,而不用关心如何管理内存,需要用多少内存,这些STL容器极大地方便了C++程序的编写。例如可以通过以下语句创建一个vector,它实际上是一个按需增长的动态数组,其每个元素的类型为int整型:

stl::vector<int> array;

拥有这样一个动态数组后,用户只需要调用push_back方法往里面添加对象,而不需要考虑需要多少内存:

array.push_back(10); 
array.push_back(2);

vector会根据需要自动增长内存,在array退出其作用域时也会自动销毁占有的内存,这些对于用户来说是透明的,stl容器巧妙的避开了繁琐且易出错的内存管理工作。

隐藏在这些容器后的内存管理工作是通过STL提供的一个默认的allocator实现的。当然,用户也可以定制自己的allocator,只要实现allocator模板所定义的接口方法即可,然后通过将自定义的allocator作为模板参数传递给STL容器,创建一个使用自定义allocator的STL容器对象,如:

stl::vector<int, UserDefinedAllocator> array;

大多数情况下,STL默认的allocator就已经足够了。这个allocator是一个由两级分配器构成的内存管理器,当申请的内存大小大于128byte时,就启动第一级分配器通过malloc直接向系统的堆空间分配,如果申请的内存大小小于128byte时,就启动第二级分配器,从一个预先分配好的内存池中取一块内存交付给用户,这个内存池由16个不同大小(8的倍数,8~128byte)的空闲列表组成,allocator会根据申请内存的大小(将这个大小round up成8的倍数)从对应的空闲块列表取表头块给用户。

这种做法有两个优点:

1)小对象的快速分配。小对象是从内存池分配的,这个内存池是系统调用一次malloc分配一块足够大的区域给程序备用,当内存池耗尽时再向系统申请一块新的区域,整个过程类似于批发和零售,起先是由allocator向总经商批发一定量的货物,然后零售给用户,与每次都总经商要一个货物再零售给用户的过程相比,显然是快捷了。当然,这里的一个问题时,内存池会带来一些内存的浪费,比如当只需分配一个小对象时,为了这个小对象可能要申请一大块的内存池,但这个浪费还是值得的,况且这种情况在实际应用中也并不多见。

2)避免了内存碎片的生成。程序中的小对象的分配极易造成内存碎片,给操作系统的内存管理带来了很大压力,系统中碎片的增多不但会影响内存分配的速度,而且会极大地降低内存的利用率。以内存池组织小对象的内存,从系统的角度看,只是一大块内存池,看不到小对象内存的分配和释放。

实现时,allocator需要维护一个存储16个空闲块列表表头的数组free_list,数组元素i是一个指向块大小为8*(i+1)字节的空闲块列表的表头,一个指向内存池起始地址的指针start_free和一个指向结束地址的指针end_free。空闲块列表节点的结构如下:

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

这个结构可以看做是从一个内存块中抠出4个字节大小来,当这个内存块空闲时,它存储了下个空闲块,当这个内存块交付给用户时,它存储的时用户的数据。因此,allocator中的空闲块链表可以表示成:obj* free_list[16];

allocator分配内存的算法如下:

算法:allocate 
输入:申请内存的大小size 
输出:若分配成功,则返回一个内存的地址,否则返回NULL 

    if(size大于128){ 启动第一级分配器直接调用malloc分配所需的内存并返回内存地址; 
    else { 
        将size向上round up成8的倍数并根据大小从free_list中取对应的表头free_list_head; 
        if(free_list_head不为空){ 
            从该列表中取下第一个空闲块并调整free_list; 
            返回free_list_head; 
        } 
     } else { 
        调用refill算法建立空闲块列表并返回所需的内存地址; 
    } 
}

重新填充算法:

算法: refill 
输入:内存块的大小size 
输出:建立空闲块链表并返回第一个可用的内存块地址 

    调用chunk_alloc算法分配若干个大小为size的连续内存区域并返回起始地址chunk和成功分配的块数nobj; 
    if(块数为1)直接返回chunk; 
    else{ 
        开始在chunk地址块中建立free_list; 
        根据size取free_list中对应的表头元素free_list_head; 
        将free_list_head指向chunk中偏移起始地址为size的地址处, 即free_list_head=(obj*)(chunk+size); 
        再将整个chunk中剩下的nobj-1个内存块串联起来构成一个空闲列表; 
        返回chunk,即chunk中第一块空闲的内存块; 
      } 
   }

块分配算法

算法:chunk_alloc 
输入:内存块的大小size,预分配的内存块块数nobj(以引用传递) 
输出:一块连续的内存区域的地址和该区域内可以容纳的内存块的块数 

    计算总共所需的内存大小total_bytes; 
    if(内存池中足以分配,即end_free - start_free >= total_bytes) { 
        则更新start_free; 
        返回旧的start_free; 
    }  
    else if(内存池中不够分配nobj个内存块,但至少可以分配一个){ 
        计算可以分配的内存块数并修改nobj; 
        更新start_free并返回原来的start_free; 
     } 
     else { //内存池连一块内存块都分配不了 
         先将内存池的内存块链入到对应的free_list中后; 
         调用malloc操作重新分配内存池,大小为2倍的total_bytes加附加量,start_free指向返回的内存地址; 
         if(分配不成功) { 
              if(16个空闲列表中尚有空闲块) 
                   尝试将16个空闲列表中空闲块回收到内存池中再调用chunk_alloc(size, nobj); 
              else  
                   调用第一级分配器尝试out of memory机制是否还有用; 
          } 
      } 
      更新end_free为start_free+total_bytes,heap_size为2倍的total_bytes; 
      调用chunk_alloc(size,nobj); 
}

假设这样一个场景,free_list[2]已经指向了大小为24字节的空闲块链表,如图1所示,当用户向allocator申请21字节大小的内存块时,allocaotr会首先检查free_list[2]并将free_list[2]所指的内存块分配给用户,然后将表头指向下一个可用的空闲块,如图2所示。注意,当内存块在链表上是,前4个字节是用作指向下一个空闲块,当分配给用户时,它是一块普通的内存区。

图1   某时刻allocator的状态

图2 分配24字节大小的内存块

http://blog.csdn.net/adcxf/article/details/6437880

STL内存管理器的分配策略的更多相关文章

  1. Netty内存管理器ByteBufAllocator及内存分配

    ByteBufAllocator 内存管理器: Netty 中内存分配有一个最顶层的抽象就是ByteBufAllocator,负责分配所有ByteBuf 类型的内存.功能其实不是很多,主要有以下几个重 ...

  2. C++STL内存管理方法(g++版)

    STL作为C++的经典作品,一直备受人们关注.本文主要介绍STL的内存管理策略. 早期的STL内存管理 第一次接触STL源码是看侯捷先生的<STL源码剖析>,此书通俗易懂,剖析透彻,是极佳 ...

  3. STL内存管理

    1. 概述 STL Allocator是STL的内存管理器,也是最低调的部分之一,你可能使用了3年stl,但却不知其为何物. STL标准如下介绍Allocator the STL includes s ...

  4. TaskTracker节点上的内存管理器

    Hadoop平台的最大优势就是充分地利用了廉价的PC机,这也就使得集群中的工作节点存在一个重要的问题——节点所在的PC机内存资源有限(这里所说的工作节点指的是TaskTracker节点),执行任务时常 ...

  5. STL内存配置器

    一.STL内存配置器的总体设计结构 1.两级内存配置器:SGI-STL中设计了两级的内存配置器,主要用于不同大小的内存分配需求,当需要分配的内存大小大于128bytes时, 使用第一级配置器,否则使用 ...

  6. SGI STL内存配置器存在内存泄漏吗?

    阅读了SGI的源码后对STL很是膜拜,很高质量的源码,从中学到了很多.温故而知新!下文中所有STL如无特殊说明均指SGI版本实现. STL 内存配置器 STL对内存管理最核心部分我觉得是其将C++对象 ...

  7. PHP V5.2 中的新增功能,第 1 部分: 使用新的内存管理器

    PHP V5.2:开始 2006 年 11 月发布了 PHP V5.2,它包括许多新增功能和错误修正.它废止了 5.1 版并被推荐给所有 PHP V5 用户进行升级.我最喜欢的实验室环境 —— Win ...

  8. C/C++内存管理器

    C标准库提供了malloc,free,calloc,realloc,C++标准库还提供了new, new[], delete, delete[].这些用来管理内存,看起来够用了,为啥还要自己写一个内存 ...

  9. spark内存管理器--MemoryManager源码解析

    MemoryManager内存管理器 内存管理器可以说是spark内核中最重要的基础模块之一,shuffle时的排序,rdd缓存,展开内存,广播变量,Task运行结果的存储等等,凡是需要使用内存的地方 ...

随机推荐

  1. hdu 1282回文数猜想

    http://acm.hdu.edu.cn/showproblem.php?pid=1282 Problem Description 一个正整数,如果从左向右读(称之为正序数)和从右向左读(称之为倒序 ...

  2. HDU4707:Pet(DFS)

    Problem Description One day, Lin Ji wake up in the morning and found that his pethamster escaped. He ...

  3. Hadoop在Windows下的安装配置

    由于本人近期近期一段时间 都在学习Hadoop,接触了比較多的理论,可是想要深入的去学习Hadoop整个平台,那就必须实战的训练,首先第一步,当然是先搭建好一个Hadoop平台为先.可是比較坑爹的是. ...

  4. 使用struts2和poi导出excel文档

    poi眼下应该是比較流行的操作excel的工具了.这几天做了个struts2和poi结合使用来实现导出excel的功能.个人认为还是比較有用的.代码阅读起来也非常easy.下来就来分享下我的心得 1  ...

  5. JSP 网页格式判定执行哪一块html

    JSP 网页格式判定执行哪一块html <!-- start -->             <td height="166" colspan="3&q ...

  6. [REST Jersey] @QueryParam Demo

    This demo sourced from the jersey tutor. https://jersey.java.net/documentation/latest/jaxrs-resource ...

  7. jquery 获取 TABLE单元格的值

    1.JQ部分: var tds = $("#table1 td"); tds.click(function(){ //给所有td添加点击事件        var tdSeq = ...

  8. SIMPASS技术解析

    一.什么叫SIMPASS SIMpass技术融合了DI卡技术和SIM卡技术,或者称为双界面SIM卡.SIMpass是一种多功能的SIM卡,支持接触与非接触两个工作接口,接触界面实现SIM功能,非接触界 ...

  9. 基于visual Studio2013解决C语言竞赛题之1058打印数字

       题目 解决代码及点评 /************************************************************************/ /* 58 ...

  10. [jQuery] check if an id exists - Google 网上论坛

    [jQuery] check if an id exists - Google 网上论坛 From: http://docs.jquery.com/Frequently_Asked_Questions ...