上节在学习第二级配置器时了解了第二级配置器通过内存池与自由链表来处理小区块内存的申请。但只是对其概念进行点到为止的认识,并未深入探究。这节就来学习一下自由链表的填充和内存池的内存分配机制。

refill()函数——重新填充自由链表

  前情提要,从上节第二级配置器的源码中可以看到,在空间配置函数allocate()中,当所需的某号自由链表为空时,才会调用refill()函数来填充链表。refill()函数默认申请20块区块的内存(5行),但所得内存不一定就是20块,要看当前内存池的剩余情况和堆容量的情况,这个在学习chunk_alloc()函数时会详解讨论,然后将第零块返回给allocate()函数,allocate()函数再返回给真正申请内存的用户,剩余内存分成区块串接成自由链表。

 template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{
int nobjs = ; //需要填充自由链表时,尝试分配20个区块作为自由链表的新结点
char * chunk = chunk_alloc(n, nobjs); //交给chunk_alloc去分配内存,后述
obj * __VOLATILE * my_free_list;
obj * result;
obj * current_obj, * next_obj;
int i;
if ( == nobjs) return(chunk); //如果只分配到1个区块,则直接返回给申请者,自由链表无新结点
my_free_list = free_list + FREELIST_INDEX(n);
/* Build free list in chunk */
result = (obj *)chunk; //如果分配到大于1个区块,即将第0个区块返回给申请者,剩下的作为自由链表的空闲区块
*my_free_list = next_obj = (obj *)(chunk + n); //从新分配内存的第一个区块开始串接成自由链表
for (i = ; ; i++) {
current_obj = next_obj;
next_obj = (obj *)((char *)next_obj + n);
if (nobjs - == i) {
current_obj -> free_list_link = ;
break;
} else {
current_obj -> free_list_link = next_obj;
}
}
return(result);
}

chunk_alloc()函数——内存池

  其实内存池仅仅是靠三个静态成员变量来管理的,并没有多复杂的机制:

static char *start_free;    //内存池起始指针
static char *end_free; //内存池的尾指针
static size_t heap_size; //多次调用内存池, 就会在内存池中分配更多的内存, 这就是一个增量.

  这在上节的第二级配置器源码中也有提到。真正复杂的是为内存池的分配内存和自由链表与内存池的交互上,这两个在chunk_alloc()上有所体现。

 template <bool threads, int inst>
char*
__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int&
nobjs) //注意nobjs是个引用
{
char * result;
size_t total_bytes = size * nobjs; //计算要申请的内存大小
size_t bytes_left = end_free - start_free; //计算当前内存池剩余大小
if (bytes_left >= total_bytes) { //如果当前内存池大小大于等于申请的大小,直接返回所需内存
result = start_free;
start_free += total_bytes;
return(result);
}
else if (bytes_left >= size) { //如果内存池没有足够的大小,但能够供应一个以上的区块,修正能够提供的区块个数
nobjs = bytes_left / size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return(result);
}
else { //内存池连一个区块的大小都无法提供了,计算准备向堆申请的内存大小
size_t bytes_to_get = * total_bytes + ROUND_UP(heap_size >> );
// 如果当前内存池要有剩余,寻找哪号自由链表能收了它.
if (bytes_left > ) {
obj * __VOLATILE * my_free_list =
free_list + FREELIST_INDEX(bytes_left);
((obj *)start_free)->free_list_link = *my_free_list;
*my_free_list = (obj *)start_free;
}
start_free = (char *)malloc(bytes_to_get); //向堆申请空间补充内存池
if ( == start_free) { //堆空间也不足了,malloc()失败
int i;
obj * __VOLATILE * my_free_list, *p;
//尝试查看更大号的自由链表中是否有1个空闲块能供我们使用
for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if ( != p) { //如果有,把它从自由链表中划进内存池,并返回递归调用chunk_alloc()函数的结果,修正nobjs
*my_free_list = p->free_list_link;
start_free = (char *)p;
end_free = start_free + i;
return(chunk_alloc(size, nobjs)); //在递归调用chunk_alloc后,会发现内存池拥有了大于1个区块的内存了,可以把该区块返回给refill()函数
}
}
//堆空间没内存,其它自由链表也没内存,山穷水尽
end_free = ;
start_free = (char *)malloc_alloc::allocate(bytes_to_get); //调用第一级配置器,期望out-of-memory机制能否尽点力
}
heap_size += bytes_to_get; //记录内存池已经开辟的堆大小
end_free = start_free + bytes_to_get;
return(chunk_alloc(size, nobjs));
}
}

  总结上述源码的功能,以end_free - start_free来判断内存池大小是否足够,如果充足则直接调出20个区块返回给自由链表。如果不足20个区块但还足够供应一个以上的区块,就拔出这不足20个区块的空间出去。这时pass by reference的nobjs将被修改为实际能够供应的区块数。如果内存池连一个区块空间都无法提供,此时便需利用malloc(),从堆中配置内存,为内存池注入足够的内存以应付需求,这足够的内存(22行)是指需求量的两倍再加上随着配置次数增加而越来越大的附加量(heap_size),如果能通过malloc()申请到40块内存,就将第前20块给自由链表,后20块放进内存池备用。

  举个实际的例子,让我们对这段源码有更深刻的认识:假设程序一开始,客端调用chunk_alloc(32, 20),于是malloc()配置40个32bytes区块,其中第一个交出,另外19个交给free_list[3]维护,剩余的20个留给内存池。接下来客端调用chunk_alloc(64, 20),此时free_list[7]空空如也,必须向内存池要求支持。内存池只够供应(32*60)/64=10个64bytes区块,就把这10个区块返回,第1个交给客端,余下交由free_list[7]维护。此时内存池空空如也。再调用chunk_alloc(96, 20),free_list[11]也空空如也,必须向内存池要求支持,而内存池表示爱莫能助,只能再向堆申请40+n个96bytes区块的内存,其中第1个交出,另外19个交给free_list[11]维护,余20+n个区块留给内存池......

  万一真的山穷水尽,整个堆空间都不够了,malloc()失败,chunk_alloc()就四处寻找有无“有未用且足够大区块”的自由链表。找到了就挖一块交出,找不到就调用第一级配置器。第一级配置器也使用malloc()来配置内存,但它有out-of-memory处理机制,或许有机会释放其它内存拿来此处使用。如果可以就成功,不可以就发出bad_alloc异常。

STL源码剖析——空间配置器Allocator#3 自由链表与内存池的更多相关文章

  1. STL源码剖析 — 空间配置器(allocator)

    前言 以STL的实现角度而言,第一个需要介绍的就是空间配置器,因为整个STL的操作对象都存放在容器之中. 你完全可以实现一个直接向硬件存取空间的allocator. 下面介绍的是SGI STL提供的配 ...

  2. STL源码剖析——空间配置器Allocator#2 一/二级空间配置器

    上节学习了内存配置后的对象构造行为和内存释放前的对象析构行为,在这一节来学习内存的配置与释放. C++的内存配置基本操作是::operator new(),而释放基本操作是::operator del ...

  3. STL源码剖析——空间配置器Allocator#1 构造与析构

    以STL的运用角度而言,空间配置器是最不需要介绍的东西,因为它扮演的是幕后的角色,隐藏在一切容器的背后默默工作.但以STL的实现角度而言,最应该首先介绍的就是空间配置器,因为这是这是容器展开一切运作的 ...

  4. STL源码剖析(空间配置器)

    前言 在STL中,容器的定义中都带一个模板参数,如vector template <class T, class Alloc = alloc> class vector {...} 其中第 ...

  5. STL源码剖析:配置器

    作用:对内存的管理 接口:申请和释放 内容: 几个全局函数 一级配置器 二级配置器 准备知识 POD是什么: Plain Old Data简称POD,表示传统的C语言类型:与POD类型对应的是非POD ...

  6. STL学习笔记:空间配置器allocator

    allocator必要接口: allocator::value_type allocator::pointer allocator::const_pointer allocator::referenc ...

  7. 《STL源码剖析》相关面试题总结

    原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...

  8. 面试题总结(三)、《STL源码剖析》相关面试题总结

    声明:本文主要探讨与STL实现相关的面试题,主要参考侯捷的<STL源码剖析>,每一个知识点讨论力求简洁,便于记忆,但讨论深度有限,如要深入研究可点击参考链接,希望对正在找工作的同学有点帮助 ...

  9. STL源码剖析之空间配置器

    本文大致对STL中的空间配置器进行一个简单的讲解,由于只是一篇博客类型的文章,无法将源码表现到面面俱到,所以真正感兴趣的码农们可以从源码中或者<STL源码剖析>仔细了解一下. 1,为什么S ...

随机推荐

  1. ICEM-带把圆环

    原视频下载地址:https://pan.baidu.com/s/1pKSXyR5 密码: dynm

  2. BS4库详解

    from bs4 import BeautifulSoup html = """ <html><head><title>This is ...

  3. 记一次有惊无险的 JVM 优化经历

    转载:https://my.oschina.net/u/3627055/blog/2995973 背景 生产环境有二台阿里云服务器,均为同一时期购买的,CPU.内存.硬盘等配置相同.具体配置如下: 节 ...

  4. #C++初学记录(贪心算法#二分查找)

    D - Aggressive cows 农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,...,xN (0 < ...

  5. Page directive: invalid value for import

    原有项目启动正常,正常访问:后来换成tomcat7.0.70:后启动正常,登陆正常,然而点进去任何菜单都会报错: java.lang.IllegalArgumentException: Page di ...

  6. 8款超好用的SVG编辑工具用起来

    随着响应式网页的发展,对于内容呈现的要求也越来越高,尤其是图像.为了在各种设备上能实现自然伸缩或扩展而不影响图片质量,所以矢量图形(SVG)正变得越来越受欢迎. 大家都知道,在计算机图形学中,有两种主 ...

  7. SVN创建分支/合并/切换使用

    原文地址:https://blog.csdn.net/lisq037/article/details/17501327 最近接项目要求,要在svn主干上创建分支,用分支来进行程序的bug修改,而主干上 ...

  8. Spring Boot Metrics监控之Prometheus&Grafana(转)

    欢迎来到Spring Boot Actuator教程系列的第二部分.在第一部分中,你学习到了spring-boot-actuator模块做了什么,如何配置spring boot应用以及如何与各样的ac ...

  9. leetcode 11. Container With Most Water 、42. Trapping Rain Water 、238. Product of Array Except Self 、407. Trapping Rain Water II

    11. Container With Most Water https://www.cnblogs.com/grandyang/p/4455109.html 用双指针向中间滑动,较小的高度就作为当前情 ...

  10. Vue 及双向数据绑定 Vue事件 以及Vue中的ref获取dom节点

    <template> <div id="app"> <h2>{{msg}}</h2> <input type="te ...