要视具体情况讨论。
1. STL 的容器,包括 map 在内,都提供了一个 Allocator 参数。可以采取定制的 Allocator 适配你自己的场景。
map 这种基于节点的容器,一个最棒的性质就是每个节点大小固定。所以采用基于 free_list 的分配器算法且使该分配器只供 map 一人使用的话,应付碎片的效果是非常好的。
如果懒得自己写,gnu 提供了一个扩展 ext/pool_allocator 就可以拿来用。
2. 就算你用的默认的分配器 std::allocator,不同平台的 std::allocator 实现算法也不一样,也不可一概而论。
一些特别旧的 STL 里 std::allocator 是实现了二级内存池。但是现在随着 malloc 的改进,基本上都没有再这么做的了,现在的很多实现里 std::allocator 都只是 operator new 或者 malloc 的一层封装。
3. 你 map 存的什么类型也没说,要是是 map<string, vector> 这种,string 和 vector 也要插一脚进来分配内存的,这情况可就复杂了。
4. 再往底层走,operator new/malloc 的具体实现在各个平台也不一样。operator new 在绝大多数的实现中都是封装了一层 malloc,所以来看 malloc 好了。
以我自己的系统 —— amd64 Linux Mint 自带的 Glibc 库中的 malloc 为例,在申请了小块内存再释放之后,它会把这些小块的内存挂在自己的 small bin 链表中,不会急于把它们归还给操作系统(也就是说,malloc 自己在用户态搞了个内存池缓存小块内存)。而且最大的问题是,就算这个 small bin 特别长,也很难激发合并操作把它们合并成大块内存,或是把它们真正归还给操作系统。所以使用了节点类容器的程序,无论是链表也好红黑树容器也好还是哈希容器也好,在过了节点个数峰值以后,程序依然会持有大量的内存不释放。
给一段测试代码,大家可以观察观察 map 和 vector 在析构以后,内存占用的变化情况有什么不同(内存小的朋友记得把代码里的数字改小一点)。在不同平台上观察到的现象可能是不同的。如果你和我一样是使用的是 Ubuntu 系的 Linux,应该可以观察到 map 释放以后程序依然持有大量内存(1.5G),然后 vector 建立时,没法使用之前已经缓存的 1.5G 的内存,是另外又向操作系统申请了 4G 的空间。再次强调一下,在不同平台上观察到的现象可能是不同的。
#include <map> #include <vector> #include <cstdio> int main() { { std::map<int, int> m; for (int i = 0; i < 32 * 1024 * 1024; ++i) { m.emplace(i, i); } printf("map will be destroyed
"); // 内存 1.5G getchar(); } printf("map has been destroyed
"); // 内存 1.5G getchar(); { std::vector<int> v; v.reserve(1024 * 1024 * 1024); for (int i = 0; i < 1024 * 1024 * 1024; ++i) { v.emplace_back(i); // 这里 emplace 的目的是要真正写内存,避免有些系统上 // 你没写就不跟你真正分配内存了 } printf("vector will be destroyed
"); // 内存 5.5G getchar(); } printf("bector has benn destroyed
"); // 内存 1.5G getchar(); }
@starwlstar vector 析构后能做到“真”释放内存而 map 做不到,并不是因为它是 vector 而它是 map。这和他们是什么数据结构是无关的。而是因为,vector 内部持有的是大段的内存。如果你建立的是非常多的长度比较短的 vector 的话,析构以后,内存一样是会被 malloc 缓存住的
这种算是比较极端的碎片例子了。
5. 另外呢,如果对系统底层的内存分配策略不满意的话,同样也有黑科技可以覆盖掉系统提供的默认的 operator new/malloc 实现。
C++ 有标准语法可以置换掉默认的 operator new / operator delete,详情可了解重载 operator new。
C 的话一些编译器提供了强弱符号功能。他们把标准的 malloc free 标记成弱符号,允许用户使用同名的强符号函数去替换掉默认的 malloc free。比如现在兴起了一批以 Google 的 TCMalloc 为代表的新一代 malloc 实现,有兴趣可以去看看这些 malloc 的官方 tuition,了解该怎么替换。
总结,你这个问题太宽泛了,不太好聊。和其他答主的观点一样,我觉得要是不是特别极端的场景、特别苛刻的要求,系统默认提供的策略已经是够用了。不过要是你关心这些问题的话,改进思路我也告诉你了,替换 Allocator 模板参数、替换 operator new、替换 malloc,都可以。
雌性动物眼中, 雄性动物漂亮/有吸引力.
雄性动物眼中, 雌性动物漂亮/有吸引力.
男性眼中, 女性漂亮/有吸引力.
女性眼中, 男性漂亮/有吸引力.
默猜题主是男人, 或雌性动物.
雄性孔雀, 颜色丰富; 那么女性是否比男性颜色更多?
雌性猴子的红屁股, 算好看算不好看?