内存基本处理工具:
STL定义了五个全局函数,作用于未初始化空间上。这在容器的实现上很有帮助。
Construct():用于构造
Destroy():用于析构
Uninitialized_copy():POD:直接用STL算法copy();non_POD:单个元素构造construct().对char*类型元素,直接内存底层操作快速移动:memmove()效率极高
Uninitialized_fill():POD:直接用STL算法fill();non_POD:单个元素构造construct()
Uninitialied_fill_n():POD:直接用STL算法fill_n();non_POD:单个元素构造construct()
Copy():强化效率无所不用其极: has trivialoperator=: memmove()速度极快
Has non_trivial operator=: 循环处理
For(;first!=last;++first)与for(n=0;n<last-first;++n):以比较n来执行循环,比迭代器的比较执行循环速度快。(迭代器可能自定义类或内置指针等,其比较。。。)
SGI 内存池:
内存池的分类:
一、不定长内存池:优点:不需要为不同的数据创建不同的内存池,缺点是分配出去的内存池不能回收到池中(?)。代表有apr_pool,obstack。
二、定长内存池:优点:使用完立即把内存归还池中。代表有Loki, Boost。
本次以sgi stl中实现的内存池作为学习对象。由于要实现的是一个C语言的内存池,所以这里用C的描述方式。喜欢C++的朋友可以直接看源文件或者《STL源码剖析》的讲解。sgi设计了二级配置机制,第一级配置器直接使用malloc()和free()。当配置区块超过128
bytes时,则采用第一级配置器;否则采用memory pool方式。
memory pool的整体思想是维护128/8 = 16个自由链表,这里的8是小型区块的上调边界。每个自由链表串接的区块大小如下:
序号 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
串接区块 |
8 |
16 |
24 |
32 |
40 |
48 |
56 |
64 |
72 |
80 |
88 |
96 |
104 |
112 |
120 |
128 |
范围 |
1-8 |
9-16 |
17-24 |
25-32 |
33-40 |
41-48 |
49-56 |
57-64 |
65-72 |
73-80 |
81-88 |
89-96 |
97-104 |
105-112 |
113-120 |
121-128 |
几个过程的思路:
一、申请过程:
Code:
1. if (用户申请的内存不大于128 bytes)
2. 查找对应的链表
3. if (对应链表拥有一块以上的区块)
4. 调整链表
5. 返回第一个区块地址
6. else
7. 准备重新填充链表
8. 向内存池申请内存(指定数量的区块)
9. if (内存池申请到一个区块)
10. 返回第一个区块地址
11. else
12. 调整链表,将区块串接起来
13. 返回第一个区块地址
14. else
15. 直接用malloc()申请内存
二、释放过程:
Code:
1. if (用户释放的内存块大于128 bytes)
2. 直接用free()释放
3. else
4. 查找对应的链表
5. 回收内存
三、向内存池申请内存过程:
Code:
1. if (内存池空间完全满足需求量)
2. 调整内存池起始位置
3. 返回空间地址
4. else if (内存池空间不能完全满足需求量,但能提供一个以上的区块)
5. 计算能够提供的最大内存
6. 调整内存池起始位置
7. 返回空间地址
8. else
9. 从内存池中压缩内存
10. 收集比size大的空间
11. 递归调用,修正nobjs
12. 再次申请内存,可能抛出异常
chunk_alloc的作用是从内存池中取内存给free list,所以会竭尽全力满足用户需要。如果有足够的空间,会返回指定数量的区块,否者返回实际能提供数量的区块,或者无法满足用户需要,那么会将内存池剩下的零头编入适当的链表中,然后向系统索要空间来补充内存池。如果系统也无内存可用,则寻找free list中是否有比size大的区块,将其用来补充空间。如果到处都已无内存可用了,会再次配置空间,可能抛出异常。否则记录内存池大小。