百科问答小站 logo
百科问答小站 font logo



C++对一个map不断insert delete元素(多任务中的一个任务),是否存在内存碎片问题? 第1页

  

user avatar   peter-43-43-80 网友的相关建议: 
      

要视具体情况讨论。

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,都可以。


user avatar   moobot_cn_robin 网友的相关建议: 
      

雌性动物眼中, 雄性动物漂亮/有吸引力.

雄性动物眼中, 雌性动物漂亮/有吸引力.

男性眼中, 女性漂亮/有吸引力.

女性眼中, 男性漂亮/有吸引力.


默猜题主是男人, 或雌性动物.


雄性孔雀, 颜色丰富; 那么女性是否比男性颜色更多?

雌性猴子的红屁股, 算好看算不好看?




  

相关话题

  单精度浮点数中的「单」和「浮点」是什么意思? 
  C++ protected继承和private继承是不是没用的废物? 
  为什么没有新的支持底层达到类似C++这种程度,而易用性达到C#的语言出现? 
  C语言编译器哪个好用? 
  编译器生成的汇编语句执行顺序为什么与C代码顺序不同? 
  如果有一个按钮按下,世界上所有的c,c++,c#语言代码都无效,我们的生活会发生什么? 
  计算机大牛们,看C++有关书籍是不是一遍就看懂了,总感觉自己笨,有些地方需要看几遍才懂? 
  C++异常处理写的代码太丑怎么办? 
  总是纠结于编程语言标准怎么办? 
  如何理解互斥锁、条件锁、读写锁以及自旋锁? 

前一个讨论
过去一百年间,有哪些生物的器官明显退化了?
下一个讨论
柯蒂斯·李梅相比于他的前辈和同僚是否具有更高的道德水平?





© 2024-12-25 - tinynew.org. All Rights Reserved.
© 2024-12-25 - tinynew.org. 保留所有权利