下面以一个例子来说明内存分配的原理:

情况一、malloc小于128k的内存,使用brk分配内存,将_edata往高地址推(只分配虚拟空间,不对应物理内存(因此没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系),如下图:

1、进程启动的时候,其(虚拟)内存空间的初始布局如图1所示。

其中,mmap内存映射文件是在堆和栈的中间(例如libc-2.2.93.so,其它数据文件等),为了简单起见,省略了内存映射文件。

_edata指针(glibc里面定义)指向数据段的最高地址。

2、进程调用A=malloc(30K)以后,内存空间如图2:

  malloc函数会调用brk系统调用,将_edata指针往高地址推30K,就完成虚拟内存分配。

你可能会问:只要把_edata+30K就完成内存分配了?

事实是这样的,_edata+30K只是完成虚拟地址的分配,A这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配了A这块内容,然后从来不访问它,那么,A对应的物理页是不会被分配的。

3、进程调用B=malloc(40K)以后,内存空间如图3。
图(1) 图(2)图(3)

情况二、malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配(对应独立内存,而且初始化为0),如下图:

图(4) 图(5)图(6)

4、进程调用C=malloc(200K)以后,内存空间如图4:

默认情况下,malloc函数分配内存,如果请求内存大于128K(可由M_MMAP_THRESHOLD选项调节),那就不是去推_edata指针了,而是利用mmap系统调用,从堆和栈的中间分配一块虚拟内存。

这样子做主要是因为:

brk分配的内存需要等到高地址内存释放以后才能释放(例如,在B释放之前,A是不可能释放的,这就是内存碎片产生的原因,什么时候紧缩看下面),而mmap分配的内存可以单独释放。

当然,还有其它的好处,也有坏处,再具体下去,有兴趣的同学可以去看glibc里面malloc的代码了。

5 、进程调用D=malloc(100K)以后,内存空间如图5;

6 、进程调用free(C)以后,C对应的虚拟内存和物理内存一起释放。

图(7) 图(8)图(9)

7、进程调用free(B)以后,如图7所示:

B对应的虚拟内存和物理内存都没有释放,因为只有一个_edata指针,如果往回推,那么D这块内存怎么办呢?

当然,B这块内存,是可以重用的,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去了。

8、进程调用free(D)以后,如图8所示:

B和D连接起来,变成一块140K的空闲内存。

9、默认情况下:

当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图9所示。


头文件

/usr/include/malloc.h

int mallopt (int __param, int __val);

参数 __param的取值范围:-8<=param<=-1 || 1<=param<=4 如下:

#define M_TRIM_THRESHOLD    -1
#define M_TOP_PAD           -2
#define M_MMAP_THRESHOLD    -3
#define M_MMAP_MAX          -4
#define M_CHECK_ACTION      -5
#define M_PERTURB           -6
#define M_ARENA_TEST        -7
#define M_ARENA_MAX         -8#ifndef M_MXFAST
# define M_MXFAST  1    /* maximum request size for "fastbins" */
#endif
#ifndef M_NLBLKS
# define M_NLBLKS  2    /* UNUSED in this malloc */
#endif
#ifndef M_GRAIN
# define M_GRAIN   3    /* UNUSED in this malloc */
#endif
#ifndef M_KEEP
# define M_KEEP    4    /* UNUSED in this malloc */
#endif

参数 __value的取值: https://www.linuxjournal.com/article/6390

Table 1. mallopt() Parameters Mapped to Environment Variables

mallopt() param选择 bash环境变量 value默认推荐值 备注
M_TRIM_THRESHOLD MALLOC_TRIM_THRESHOLD_ 128KB -1U disables
M_TOP_PAD MALLOC_TOP_PAD_ 0
M_MMAP_THRESHOLD MALLOC_MMAP_THRESHOLD_ 128KB 0 disables
M_MMAP_MAX MALLOC_MMAP_MAX_ 64 0 disables

函数返回值1表示成功,0表示失败并修改全局变量errno;

头文件malloc.h:函数 mallopt()的选项相关推荐

  1. C11头文件threads.h声明了创建和管理线程,信号,条件变量的函数

    作者Danny Kalev 是通过以色列系统分析师协会认证的系统分析师, 并且是专攻C++的软件工程师. Kalev 写了多本C++的书籍,同时给不同的软件开发者站点投搞C++文章. 他是C++标准委 ...

  2. c语言中关于不包含头文件string.h仍然可以使用strlen函数以及strlen函数计算数组时结果问题

    先说头文件string.h里的strlen函数 strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串 ...

  3. 习题 8.4 在本章第8.3.3节中分别给出了包含类定义的头文件student.h,包含成员函数定义的源文件student.cpp以及包含主函数的源文件main.cpp。请完善该程序,在类中增加。。。

    C++程序设计(第三版) 谭浩强 习题8.4 个人设计 习题 8.4 在本章第8.3.3节中分别给出了包含类定义的头文件student.h,包含成员函数定义的源文件student.cpp以及包含主函数 ...

  4. 习题 8.5 将本章的例8.4改写为一个多文件的程序:1.将类定义放在头文件arraymax.h中;2.将成员函数定义放在源文件arraymax.cpp中;3.主函数放在源文件file1.cpp中。

    C++程序设计(第三版) 谭浩强 习题8.5 个人设计 习题 8.5 将本章的例8.4改写为一个多文件的程序: 1.将类定义放在头文件arraymax.h中: 2.将成员函数定义放在源文件arraym ...

  5. c语言gets函数头文件string.h,string函数头文件

    string.h头文件中有什么函数 string.h头文件中的函数: #include //STL 通用算法 #include //STL 位集容器 #include //复数类 #include / ...

  6. C++编程常用头文件及其包含函数汇总

    C++编程常用头文件及其包含函数汇总 1.#include <iostream> #include<iostream>是标准的C++头文件,任何符合标准的C++开发环境都有这个 ...

  7. 头文件setjmp.h

    头文件<setjmp.h>定义了宏setjmp,并且为了绕过正常的函数调用和返回规则声明了一个函数和一个类型. 1.类型jmp_buf 它是一个数组类型,适合存储恢复一个调用环境所需的信息 ...

  8. 【转】C++中#include包含头文件带 .h 和不带 .h 的区别

    C++中#include包含头文件带 .h 和不带 .h 的区别? 如 #include <iostream> 和 #include <iostream.h> 包含的东西有哪些 ...

  9. 【C++】C++中的头文件(.h)—详解(2)

    接上... [fishing-pan:https://blog.csdn.net/u013921430转载请注明出处] 头文件中写些什么 在上篇博客中写到头文件本身不参与编译,但是它们被包含到源文件中 ...

最新文章

  1. Frameless - 用于预览 iOS8 原型的浏览器
  2. formSelects-v4.js 基于Layui的多选解决方案
  3. SQL server2017和ssms管理工具下载
  4. 一些简单有趣的c语言编程,一个有趣的小程序
  5. http://blog.seirsoft.com
  6. 声道测试音频_功率放大器测试方法
  7. mqtt+uniapp 发布/订阅实例
  8. zero to one (2)
  9. 频谱分析_滚动轴承的频谱分析
  10. 八种排序java实现
  11. 利用Python解决掉谷歌人机验证,全自动识别真的牛啊
  12. phpmywind教程:单页信息调用说明【进阶篇一】
  13. C#中的NewLine到底是什么字符?
  14. HTML Table之展开收起
  15. html获取问号后的参数,html问号后的值怎么获取
  16. ZYNQ之路--搞清楚Xilinx开发软件之间的关系
  17. Adobe Acrobat DC卸载更新出现问题,重装更新显示找不到源而反复出现window installer 安装失败(开机弹窗,升级不了)解决方法,亲测有效!!!
  18. 迅如疾风 PHPWind 6.3.2 测试手记
  19. 天涯上的一篇恶搞武侠贴:如何成为大侠
  20. 专业气压表胎压计解决方案开发

热门文章

  1. 多线程下载Android
  2. python的os为啥找不到文件_python使用os.listdir和os.walk获得文件的路径
  3. Linux:sudo命令实例讲解
  4. L1-031 到底是不是太胖了 (10 分)
  5. 行内块的巧妙运用(HTML、CSS)
  6. 天梯—是不是太胖了(C语言)
  7. 天梯—谁先倒(C语言)
  8. UVW 在C#中的代码实现
  9. JavaScript常用数组操作方法,包含ES6方法
  10. 阿里云前端周刊 - 第 37 期