在linux下调用malloc()分配内存的时候,实际占用的内存与请求的内存尺寸的关系是什么呢,这个需要研究一下glibc中malloc()的实现。现在常见linux发行版中带的glibc中采用的都是Doug Lea的实现,下面的分析取自他的2..4版本的malloc.c。

glibc对内存的管理是以chunk为单位的,未分配的chunk之间用双向链表连接成一个环,遍历的时候用指针遍历,已分配的chunk在其首部有chunk的大小,并以此字节数做遍历。每个chunk的首部要放置一个叫做malloc_chunk的struct,故每个chunk的大小至少是这个struct的大小,如果分配的空间或者free的空间大于这个大小,则struct的后面是raw的数据或者是空白空间。chunk的定义如下所示:
struct malloc_chunk {
size_t prev_foot; /* Size of previous chunk (if free). */
size_t head; /* Size and inuse bits. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
}; 对于已分配的和未分配的chunk,使用的都这个结构,只是用到的成员不一样。对于这两种情况,head变量存放的都是本chunk的尺寸,由于chunk的大小有对齐的规定(见下文),所以head变量的最后三位一定是0的,用不到,所以这三位被做为三个标志位使用,其实本文涉及的是最低位P,它表示当前chunk紧跟着的上一个chunk是不是一个free的chunk,以及倒数第三位A,它表明当前chunk是否被使用中。如果上一个chunk是free的,prev_foot变量就是有用的,它表明了上一个chunk的尺寸,如果上一个chunk也是分配了的,prev_foot变量就是不使用的(在内存上直接和前一个chunk重叠起来,见下文的图示)。如果当前chunk是个空chunk,那么fd和bk两个指针就会分别指向空闲chunk环中的上一个和下一个,如果当前chunk已经分配了,fd和bk所在的内存存放的就是用户实际申请到的数据空间了。 Doug Lea在malloc.c的源代码中画了图来描述实际分配中的内存使用情况,首先是对于已分配的chunk:
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk (if P = ) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of this chunk || |P|+
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+- -+
| |
+- -+
| :
+- size - sizeof(size_t) available payload bytes -+
: |
chunk-> +- -+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk (may or may not be in use) ||+
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 图中,chunk箭头所指的位置是malloc_chunk struct的起始位置,mem箭头所指的位置是malloc()等函数返回给用户的指针的位置,也就是实际数据的位置,mem和chunk箭头之前的2*size_t字节的空间可认为是overhead。但是注意第二个chunk,它的上一个chunk也就是第一个chunk是已经分配的,所以它的head字段的P标志位为1,并且它的prev_foot字段其实是不存在的,因为它和第一个chunk的payload数据的最后size_t个字节是重叠的。如果第二个chunk也被分配了的话,它的overhead就是第二个mem箭头上面的那一条当前chunk尺寸(也就是head变量)的那size_t字节的空间。 对于空闲的chuck是这样的:
chunk-> +- -+
| User payload (must be in use, or we would have merged!) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of this chunk || |P|+
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Prev pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+- size - sizeof(struct chunk) unused bytes -+
: |
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of this chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk (must be in use, or we would have merged||+
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :
+- User payload -+
: |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 如图中注释所说,两个free的chunk如果是相邻的,会被合并,所以不会存在两个free的chunk相连的情况。(对于较小的刚刚被free的chunk,glibc中存在一种调度算法,把这种小free chunk加入一个叫fastbins的队列管理,这时尽管被free,它们的A标志位会保持为1,因此就算相邻也不会被合并)。 注意图中的第二个chunk,它是一个被分配了的chunk,它的第一个字段prev_foot表示的是第一个free的chunk的大小。其实这个prev_foot变量也是位于上一个chunk的尾部。真正属于第二个chunk的空间还是从“size of the next chunk”开始的,它距离用户数据的空间mem是size_t字节。 综合上面两个例子可以看出,对于一个已分配了的chunk,如果它的上一个chunk也是分配了的,它的prev_foot就是完全被前一个chunk覆盖的;如果它的上一个chunk是未分配的,它的prev_foot存的是该空间chunk的大小,且prev_foot变量位于前一个chunk的最后size_t个字节。这样看来,已分配的chunk的第一个成员prev_foot总是位于别的chunk内的,所以overhead就是head一个变量的大小,即site_t字节。这样一种chunk之间重叠的设计,使得prev_foot与其说是当前chunk的第一个变量,不如说是上一个chunk结尾尺寸标记(从它的名字foot就可以看出这一点)。这使得对于一个空闲chunk来说,其头(自身的header变量)和尾(下一个chunk的prev_foot)都是此空闲chunk的尺寸,这使得从正向两个方向按字节数遍历都很容易,是个不错的设计。 除了上面讨论的overhead以外,chunk的存储还涉及对齐的问题,glibc中规定chunk的以size_t的两倍大小对齐。与此相关的一些代码如下:
#define MALLOC_ALIGNMENT (2 * SIZE_SZ) //在我下载的malloc.c中不是这样定义的,而是分情况讨论的
#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) #define CHUNK_OVERHEAD (SIZE_T_SIZE) #define MCHUNK_SIZE (sizeof(mchunk)) /* The smallest size we can malloc is an aligned minimal chunk */
#define MIN_CHUNK_SIZE\
((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* pad request bytes into a usable size */
#define pad_request(req) \
(((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* pad request, checking for minimum (but not maximum) */
#define request2size(req) \
(((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) request2size这个宏汇合了上文的各个因素,给出一个要申请的空间大小,它返回的就是实际分配的内存大小。只申请1个字节的时候,实际至少也要分配malloc_chunk的大小那么多空间,当申请的字节更多直到一个malloc_chunk的空间放不下后,实际空间会以2倍的size_t为步长增长。 这里写了一个程序验证:
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h> #define N 1 int main() {
int i = ;
char *p;
char *p0, *p1; size_t *psize; for (i = ; i < ; i++) {
p = (char*) malloc(N);
if (i == ) p0 = p;
if (i == ) p1 = p;
}
printf("%d\n", p1 - p0); //看两个chunk的mem指针相差多少 psize = (size_t*)(p1 - sizeof(size_t));
printf("%d\n", (*psize) & ~); //看head变量的值,& ~7的作用是把后三个标志位过滤掉 getchar();
return ; } 在32系统下,size_t和指针均为4字节,当N为1至12时,程序的内存占用为16M,当N为13时,程序占用内存涨到24M。
在64系统下,size_t和指针均为8字节,当N为1至24时,程序的内存占用为32M,当N为25时,程序占用内存涨到48M。 上述程序在centos .3下,gcc 4.1.2下测试通过。另外,由于C++的new操作底层调用的也是malloc,所以上述讨论对new同样适用。 最后附上本文主要的参考材料,一个讲述glibc的pdf和malloc.c源代码。

http://blog.loudly.me/2010/02/glibc_malloc_memory_overhead/

glibc中malloc()的空间overhead的更多相关文章

  1. glibc中malloc的详细解释_转

    glibc中的malloc实现: The main properties of the algorithms are:* For large (>= 512 bytes) requests, i ...

  2. *** glibc detected *** malloc(): memory corruption 分类: C/C++ Linux 2015-05-14 09:22 37人阅读 评论(0) 收藏

    *** glibc detected *** malloc(): memory corruption: 0x09eab988 *** 发现是由于memset越界写引起的. 在Linux Server上 ...

  3. 一步一步pwn路由器之uClibc中malloc&&free分析

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 栈溢出告一段落.本文介绍下 uClibc 中的 malloc 和 ...

  4. House of apple 一种新的glibc中IO攻击方法

    目录 House of apple 一种新的glibc中IO攻击方法 前言 利用条件 利用原理 利用思路 思路一:修改tcache线程变量 思路二:修改mp_结构体 思路三:修改pointer_gua ...

  5. c中malloc的用法

    转自:http://blog.sina.com.cn/s/blog_966f8e8501010if7.html Malloc 向系统申请分配指定size个字节的内存空间.返回类型是 void* 类型. ...

  6. 给指针malloc分配空间后就等于数组吗?

    首先回答这个的问题:严格的说不等于数组,但是可以认为它是个数组一样的使用而不产生任何问题.不过既然这样,那它应该算是个数组吧.所以,一般我们都用“动态数组”这种名字来称呼这种东西. 要讲清楚这个东西, ...

  7. 给指针malloc分配空间后就等于数组吗?【转】

    首先回答你的问题:严格的说不等于数组,但是可以认为它是个数组一样的使用而不产生任何问题. 不过既然这样,那它应该算是个数组吧.所以,一般我们都用“动态数组”这种名字来称呼这种东西. 要讲清楚这个东西, ...

  8. 虚拟机中Linux系统盘空间不足

    虚拟机中Linux系统盘在使用过程中, 出现空间不足的提示. 使用命令du --max-depth=1 -h 查看Home目录下各个文件占用空间, 发现是./cache(隐藏文件)占用很大空间.进入c ...

  9. Windows Azure 上 Linux VM 中的交换空间 – 第 2 部分

    本文章由 Azure CAT 团队的 Piyush Ranjan (MSFT) 撰写. 在前一篇文章 Windows Azure 上Linux VM 中的交换空间第 1 部分中,我介绍了在默认情况下, ...

随机推荐

  1. 在线分享Oracle尖峰时刻--2014年中秋节尖峰在线福利!

    **********************************************************            2014年中秋节尖峰在线福利!*************** ...

  2. iOS: 详细的正则表达式

    一.简单的正则规则 1.由数字.26个英文字母或者下划线组成的字符串: ^[-9a-zA-Z_]{,}$ 2.非负整数(正整数 + 0 ): ^/d+$ 3. 正整数: ^[-]*[-][-]*$ 4 ...

  3. hello--GAN

    GAN系列学习(1)——前生今世 DCGAN.WGAN.WGAN-GP.LSGAN.BEGAN原理总结及对比 [Learning Notes]变分自编码器(Variational Auto-Encod ...

  4. Nodejs 天涯帖子《鹿鼎记中计》 柳成萌著 下载爬虫

    功能:从天涯帖子中下载楼主发言到一个文本文件中 实验对象:http://bbs.tianya.cn/post-no05-308123-1.shtml  <鹿鼎记中计> 柳成萌著 爬取效果: ...

  5. ASP.NET HTTP404错误怎么办

    如图所示,HTTP404错误. 这是由于没有安装ASP.NET导致的.我们在控制面板->打开或关闭windows功能->Internet信息服务->万维网服务->应用程序开发功 ...

  6. Tomcat 之 启动tomcat时 错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099;

    错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099; nested exception is:  java. ...

  7. 使用 Tmux 强化终端功能

    来自 tmux是一个优秀的终端复用软件,类似GNU Screen,但来自于OpenBSD,采用BSD授权.使用它最直观的好处就是通过一个终端登录远程主机并运行tmux后,在其中可以开启多个控制台而无需 ...

  8. Swift 与 Object-C 交互 (Swift版本为:1.2)

    这篇文章主要是介绍 Swift 与 Object-C 之间进行交互的代码,主要分为两个部分.一个是 Swift 项目调用 Object-C 的类,另一个是 Object-C 项目调用 Swift 类. ...

  9. PHP-"php://(类型)"访问各个输入/输出流以及全局变量$HTTP_RAW_POST_DATA讲解

    $_POST $HTTP_RAW_POST_DATA php://input 先来讲以上三者的区别: $_POST:以关联数组方式组织提交的数据, 并对原数据进行编码处理(urldecode)和编码转 ...

  10. visio扩大画布的大小

    鼠标移到画布的边界按住Ctrl,即可扩大画布的大小