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

原因还是从性能考虑:例如malloc和new是出于通用性考虑的,能处理多线程情况(multithread)。对于单线程的程序,这种额外的功能反而降低性能。

而且还注意到,new/delete/free/malloc都是要在user-space和kernel-code做切换的,context的切换会降低性能。如果自己写一个user-land的内存管理器,就能大幅减少这种切换。还有就是GC(garbage collection)。

几点要求

  1. 速度:比编译器的内存分配器要快才行
  2. 鲁棒:不能有内存泄漏,分配多少就收回多少
  3. 方便:用户不怎么需要改代码,就能把内存管理器加进去
  4. 移植:应当跨平台,用户在啥系统上都能用,方便移植

前人经验

  1. 申请大块内存

    一次性申请一大块内存,减少向系统申请的次数,以后需要申请内存就从这一大块上分配。

    (这不就是缓存么。。)
  2. 为特定尺寸优化

    任何程序中都一种最常见的内存申请尺寸。为这种尺寸优化,提升性能。
  3. 需要删除的内存暂时存放在容器中(敝帚自珍)

    从用户角度看,变量声明周期结束,要释放分配的内存;但是内存管理器实际上可以“不真的把这块内存还给系统”,而是攒起来留给后续需要分配内存时用。当然,这种内存更多的是碎片,所以再分配时可能不够用,那就得再找大块内存去分配了。

代码,版本1

首先是一个不使用内存管理器的代码,内存的申请和释放是手动完成的,并且放在for循环中,来频繁的申请和释放,方法这种做法的效果(慢啊)。代码:

  1. #include <ctime>
  2. #include <iostream>
  3. using namespace std;
  4. class Complex
  5. {
  6. public:
  7. Complex(double a, double b) : r(a),c(b)
  8. {
  9. }
  10. private:
  11. double r; //实部
  12. double c; //虚部
  13. };
  14. int main(int argc, char* argv[])
  15. {
  16. Complex* array[1000];
  17. clock_t t1,t2;
  18. t1 = clock();
  19. for (int i = 0; i < 5000; i++)
  20. {
  21. for (int j = 0; j < 1000; j++)
  22. {
  23. array[j] = new Complex(i,j);
  24. }
  25. for (int j=0;j<1000;j++)
  26. {
  27. delete array[j];
  28. }
  29. }
  30. t2 = clock();
  31. cout << double(t2-t1)/CLOCKS_PER_SEC << "s" << endl;
  32. return 0;
  33. }

代码,版本2

这次用一个内存管理类来托管内存的申请和释放,并且原有的Complex类上仅仅是重载了new/delete/new[]/delete[]这四个operator。放码过来:

  1. #include <iostream>
  2. #include <sys/types.h>
  3. using namespace std;
  4. class IMemoryManager{
  5. public:
  6. virtual void* allocate(size_t) = 0;
  7. virtual void free(void*) = 0;
  8. };
  9. class MemoryManager: public IMemoryManager{
  10. public:
  11. MemoryManager(){
  12. freestorehead = NULL;
  13. ExpandPoolSize();
  14. }
  15. ~MemoryManager(){
  16. CleanUp();
  17. }
  18. void* allocate(size_t);
  19. void free(void*);
  20. private:
  21. struct FreeStore{
  22. FreeStore* next;
  23. };
  24. void ExpandPoolSize();
  25. void CleanUp();
  26. FreeStore* freestorehead;
  27. };
  28. MemoryManager gMemoryManager;
  29. class Complex {
  30. public:
  31. Complex(double a, double b): r(a), c(b){}
  32. inline void* operator new(size_t size){
  33. return gMemoryManager.allocate(size);
  34. }
  35. inline void operator delete(void* object){
  36. gMemoryManager.free(object);
  37. }
  38. inline void* operator new[](size_t size){
  39. return gMemoryManager.allocate(size);
  40. }
  41. inline void operator delete[](void* object){
  42. return gMemoryManager.free(object);
  43. }
  44. private:
  45. double r;
  46. double c;
  47. };
  48. void* MemoryManager::allocate(size_t size){
  49. if (0==freestorehead){
  50. ExpandPoolSize();
  51. }
  52. FreeStore* head = freestorehead;
  53. freestorehead = head->next;
  54. return head;
  55. }
  56. void MemoryManager::free(void* object){
  57. FreeStore* head = static_cast<FreeStore*>(object);
  58. head->next = freestorehead;
  59. freestorehead = head;
  60. }
  61. const int POOLSIZE = 128;
  62. void MemoryManager::ExpandPoolSize(){
  63. size_t size = max(sizeof(Complex), sizeof(FreeStore*));
  64. FreeStore* head = reinterpret_cast<FreeStore*>(new char[size]);
  65. freestorehead = head;
  66. for(int i=0; i<POOLSIZE; i++){
  67. head->next = reinterpret_cast<FreeStore*>(new char[size]);
  68. head = head->next;
  69. }
  70. head->next = 0;
  71. }
  72. void MemoryManager::CleanUp(){
  73. FreeStore* nextPtr = freestorehead;
  74. for(; nextPtr; nextPtr=freestorehead){
  75. nextPtr = freestorehead;
  76. freestorehead = freestorehead->next;
  77. delete[] nextPtr;
  78. }
  79. }
  80. int main(int argc, char* argv[])
  81. {
  82. Complex* array[1000];
  83. clock_t t1,t2;
  84. t1 = clock();
  85. for (int i = 0; i < 5000; i++)
  86. {
  87. for (int j = 0; j < 1000; j++)
  88. {
  89. array[j] = new Complex(i,j);
  90. }
  91. for (int j=0;j<1000;j++)
  92. {
  93. delete array[j];
  94. }
  95. }
  96. t2 = clock();
  97. cout << double(t2-t1)/CLOCKS_PER_SEC << "s" << endl;
  98. return 0;
  99. }

分析

时间开销对比

  1. g++ main_v1.cpp -o main_v1 -O3
  2. ./main_v1
  3. 0.217214s
  4. g++ main_v2.cpp -o main_v2 -O3
  5. ./main_v2
  6. 0.026611s

两者的时间开销竟然相差一个数量级。

代码的正确性

其实new()申请内存的代码很不严谨,没有检查形参size是否会超过预设POOLSIZE大小,只不过通常情况下单次的size肯定小于POOLSIZE,但是极端情况下,或者一口气申请了多个变量的内存,可能会越界。另外,只适用于单线程。

以上内容来自IBM的一篇教程,还有很多内容没能看完和理解,挖坑带填:https://www.ibm.com/developerworks/aix/tutorials/au-memorymanager/index.html

C/C++内存管理器的更多相关文章

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

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

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

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

  3. STL内存管理器的分配策略

    STL提供了很多泛型容器,如vector,list和map.程序员在使用这些容器时只需关心何时往容器内塞对象,而不用关心如何管理内存,需要用多少内存,这些STL容器极大地方便了C++程序的编写.例如可 ...

  4. BBS项目详解(forms快速创建登陆页面,登陆验证、通过阅读器进行头像上传的预览、内存管理器)

    BBS项目涉及的知识点 django中知识点 钩子函数(局部钩子和全局钩子) 1.局部钩子就是用来做合法性校验,比如用户名有没有被使用等 2.全局的就是用来做对比校验,比如两次输入的密码是否一致 3. ...

  5. DLL何时需共享内存管理器

    Delphi创建DLL时,IDE自动生成的文档中写得很清楚,当在DLL中以动态数组或String做为参数或返回值时(即RTL自动维护的数据类型),请在每个工程文件的第一个单元加上ShareMem.这样 ...

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

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

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

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

  8. android的低内存管理器【转】

    本文转载自:http://blog.csdn.net/haitaoliang/article/details/22092321 版权声明:本文为博主原创文章,未经博主允许不得转载. 安卓应用不用太在意 ...

  9. LevelDB(v1.3) 源码阅读之 Arena(内存管理器)

    LevelDB(v1.3) 源码阅读系列使用 LevelDB v1.3 版本的代码,可以通过如下方式下载并切换到 v1.3 版本的代码: $ git clone https://github.com/ ...

随机推荐

  1. HardNet解读

    论文:Working hard to know your neighbor’s margins: Local descriptor learning loss  为什么介绍此文:这篇2018cvpr文 ...

  2. java虚拟机的堆内存配置

    官网文档地址:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html 接录如下: -XX:MaxHeapSize=si ...

  3. keepalived 的某台vip连接不通【原创】

    keepalived 的某台vip连接不通,vip可以漂移到这台服务器,但是ping vip不通,telnet vip 3306服务也不通,但是telnet 服务器真实物理IP 3306是通的. 切换 ...

  4. PEP 530 -- 异步推导式

    PEP 530 -- 异步推导式 摘要 PEP 492和PEP 525使用async/await语法引入了协程.PEP 530建议添加list,set,dict推导式和生成器推导式的异步版本. 理论和 ...

  5. Alpha冲刺(8/10)

    目录 摘要 团队部分 个人部分 摘要 队名:小白吃 组长博客:hjj 作业博客:冲刺倒计时之8 团队部分 后敬甲(组长) 过去两天完成了哪些任务 首页重新设计 课程时间线确定 答辩准备 接下来的计划 ...

  6. cef_binary_3.2623.1401.gb90a3be

    这个资源现在很难搜索到 分享给大家 http://www.ceffans.com/forum.php?mod=viewthread&tid=9 http://pan.baidu.com/sha ...

  7. MVC异步方法

    在mvc的开发过程中,有时候我们会需要在action中调用异步方法,这个时候会需要做一些特殊处理.我们会使用到await和async.对应的controller也应该是async的. 在MVC4中直接 ...

  8. Android应用开发中三种常见的图片压缩方法

    Android应用开发中三种常见的图片压缩方法,分别是:质量压缩法.比例压缩法(根据路径获取图片并压缩)和比例压缩法(根据Bitmap图片压缩). 一.质量压缩法 private Bitmap com ...

  9. 自定义Dialog的详细步骤(实现自定义样式一般原理)

    现在很多App的提示对话框都非常有个性,然而你还用系统的对话框样式,是不是觉得很落后呢,今天我就给大家讲讲怎样自定义自己的Dialog,学会了之后,你就会根据自家app的主题,设计出相应的Dialog ...

  10. LuoGu P2863 [USACO06JAN]牛的舞会The Cow Prom

    题目传送门 这个题还是个缩点的板子题...... 答案就是size大于1的强连通分量的个数 加一个size来统计就好了 #include <iostream> #include <c ...