free函数在操作系统内存中的实现【转】
转自:http://www.2cto.com/kf/201210/160985.html
我一次性malloc十个单位节点的内存空间出来赋值给L, 现在我想一次性删除从第3个到第6个节点,我是这么做的:
1.将第六个节点的next指针指向NULL
2.将L指针指向第三个单位空间的地址,再free(L)。
等到把代码写完之后,才发现其中的问题:这里我的free(L)用的对吗?
编译运行了一下才发现了问题所在。
为了清楚地看到这个错误,将这个问题简化出来,请看下面的代码:
[cpp]
#include <stdio.h>
#include <malloc.h>
int main()
{
char *p = (char*)malloc(sizeof(char) * 10);
char *q = p;
int i = 0;
for (; i < 10; i++)
printf("%08X\n", q + i);
q = q + 2;
free(q);
return 0;
}
编译执行之后结果如下:
0863E008
0863E009
0863E00A
0863E00B
0863E00C
0863E00D
0863E00E
0863E00F
0863E010
0863E011
*** glibc detected *** ./free: free(): invalid pointer: 0x0863e00a ***
======= Backtrace: =========
/lib/libc.so.6[0x4481d9f2]
./free[0x8048487]
/lib/libc.so.6(__libc_start_main+0xf3)[0x447be6b3]
./free[0x8048391]
======= Memory map: ========
......
已放弃(吐核)
在linux下面可以看到free这句代码是不能被运行的。
那么为什么执行会出错呢?
这里首先要谈谈malloc和free这两个函数的用法了。
首先我们要知道的一个原则就是:malloc 和free是成对使用的。
你可能会奇怪为什么malloc指定了内存分配的长度,而free的时候没有指定释放的内存长度。
因为是成对使用的,所以满足下面两个原则:
1.我们free的指针必须是malloc出来返回给你的指针。
2. 所以,free的长度是你malloc要求分配的长度。
看到这里可能有人会问:你是怎么得出这两个结论的呢?
好吧,现在我们就来深究一下,free函数在操作系统内存中究竟是怎么实现的。
但是毕竟自己实力有限,还是菜鸟一枚,如果让我阅读free 和malloc 的源代码,肯定是不现实的,貌似有1500多行。。。。
自己摘除了一点基本信息:
malloc/free 是操作系统提供的接口
不同的系统可能使用不同的库
接口形式相同,但是实现方式可能不同
这主要取决于操作系统内存管理模式
VC使用的CRT,而GCC使用的是libc
里面有两段比较经典的话。虽然正确性还有待考证。摘录如下:
1.
内核通过一个红黑树来记录了空闲的内存,malloc就是从树中查找一块大小适合的内存并把地址给你,然后把这个节点从树中摘除,避免被别人分配到产生冲突。这个内存现在归你一个人用了。
free函数是把你的这个内存重新放回到红黑树中,让别人可以申请到这个内存。从逻辑上来说,你现在不能在使用这个内存了,因为它已经不属于你。但是系统的实现上目前没有做到,所以你还是能访问这个地址。
另外,系统也不会帮你覆盖内存中的数据,因为做这一个操作浪费时间,没有必要。
打一个简单的比方。你租了一套房子,后来租期到了,房子回到房东手里,或者又转租给别人。但是你拿着原来的钥匙还是能进入那套房子,虽然这个是不合法的。
2.
应该确切讲是不变的。内存管理多数是通过一个MBC链表实现的,及你实际分配的内存空间为:(nSize + 3) / 4 * 4 + sizeof(MBC)的大小,在malloc之后,系统程序实际返回的是分配的MBC地址+sizeof(MBC),释放内存时,free所做的第一个动作是ptr - sizeof(MBC)得到实际的MBC块,在这个MBC块中包含了该内存的大小,内存MBC链的指针等信息;所以,如果你使用了超出实际分配内存大小的空间,会造成整个MBC链的混乱,最直接表现是程序在free时在另一个不相关的地方出现了异常;所以您可以看出来,在执行了free之后,该块内存并没有改变,即使该块内存相邻内存为空,而发生了内存块的合并,您刚才使用的内存空间也没有发生改变(你看到的),改变的是MBC链表;
详细的内存管理程序您可以参考一些库,如dlmalloc,这个库除了使用了MBC链外,还是用了沙箱机制;事实上早期DOS的内存管理程序也是使用的MBC链;
3.
1)我注意到之上的回复多数是基于系统端的,(如红黑树,我在Understand Linux kernal中还真没有听说过红黑树,最后居然在国内网站找到了 ---- 不知道是不是只是国内学术名词,是内核的内存管理);系统内存分配涉及属性、内存页面以及是否有MCU;但是注意一点malloc和free并不是直接使用系统内存管理程序,在多数Linux程序中malloc和free一般是通过标准库,及我说过的DL内存管理程序实现的;因此,内核的内存管理和我们在应用层面的内存管理(如malloc/free)不要混为一谈;
2)DL的内存管理从效能上要比直接使用系统的内存管理效能要高,如经过我们实测:在随机大小、随机顺序申请释放 ---- 目的在制造最差情况,DL内存管理程序是直接使用系统的3~3.9倍,考虑使用realloc,DL内存管理程序是系统的10~43.35倍;所以,实际情况下,我们并不是直接使用系统内存管理 ---- 只是我们并不知道。
转自:http://blog.csdn.net/Leisure512/article/details/4978476
#include<stdio.h>
#include<stdlib.h>
int main(){
int *test=(int *)calloc(1,sizeof(int));
if(!test)
printf("分配内存错误/n");
else{
printf("指针地址:%X/n",(unsigned int)test);
printf("指向内存的内容:%d/n",*test);
printf("释放它/n");
free(test);
printf("释放后,指针的地址:%X/n",(unsigned int)test);
printf("释放后,指针指向的内存的内容:%d/n",*test);
}
return 0;
}
通过上面的小程序,可以看出,在free前后,指针test并没有改变,那么free到底怎么工作呢?
查看man 3 free,其中也没有说明白,只是说free释放掉指定参数指针指向的内存,并返回void。其实,free函数只是将参数指针指向的内存归还给操作系统,并不会把参数指针置NULL,为了以后访问到被操作系统重新分配后的错误数据,所以在调用free之后,通常需要手动将指针置NULL。从另一个角度来看,内存这种底层资源都是由操作系统来管理的,而不是编译器,编译器只是向操作系统提出申请。所以free函数是没有能力去真正的free内存的。只是告诉操作系统它归还了内存,然后操作系统就可以修改内存分配表,以供下次分配。
free函数在操作系统内存中的实现【转】的更多相关文章
- Java中内存中的Heap、Stack与程序运行的关系
堆和栈的内存管理 栈的内存管理是顺序分配的,而且定长,不存在内存回收问题:而堆 则是随机分配内存,不定长度,存在内存分配和回收的问题:堆内存和栈内存的区别可以用如下的比喻来看出:使用堆内存就象是自己动 ...
- java中的各种数据类型在内存中存储的方式
原文地址:http://blog.csdn.net/aaa1117a8w5s6d/article/details/8251456 1.Java是如何管理内存的 java的内存管理就是对象的分配和释放问 ...
- python中的函数对象的内存地址是多少
今天和同学讨论一个问题,发现了函数的内存地址和我想象的不一样. 我以为同一个函数,假如给的参数不一样,那么这两个函数的id就不一样. 然后经过实验,发现python为了便于管理函数,所有的函数都放在同 ...
- 函数的返回值是如何带出和接收的以及内存中的活动情况.RP
函数返回值时,要生成一个值的副本. 而用引用返回值时,不生成值的副本. 例如,下面的程序是有关引用返回的4种形式: //********************* //** ch9_6.cpp ** ...
- php实现 求int型数据在内存中存储时1的个数(函数都可自己实现)
php实现 求int型数据在内存中存储时1的个数(函数都可自己实现) 一.总结 一句话总结:函数我们自己都可以实现,尤其是很多基础函数,没有工具的时候自己写. 1.php进制转换函数? base_co ...
- QList介绍(QList比QVector更快,这是由它们在内存中的存储方式决定的。QStringList是在QList的基础上针对字符串提供额外的函数。at()操作比操作符[]更快,因为它不需要深度复制)非常实用
FROM:http://apps.hi.baidu.com/share/detail/33517814 今天做项目时,需要用到QList来存储一组点.为此,我对QList类的说明进行了如下翻译. QL ...
- C语言calloc()函数:分配内存空间并初始化——stm32中的应用
经常在代码中看到使用malloc来分配,然后memset清零,其实calloc更加方便,一句顶两句~ 头文件:#include <stdlib.h> calloc() 函数用来动态地分配内 ...
- C++虚函数在内存中的实现
首先来一张图,一目了然: 然后把相应的代码贴上来: class A { int a; public: virtual void f(); virtual void g(int); virtual vo ...
- 每个软件都自己把操作系统的host配置项加到内存中供频繁调用
nginx的转发,http_pass 转发到一个域名passport.ab.cn 那具体是到哪台机器上. 其实可以通过/etc/hosts文件来配置的. 可以理解,这个hosts文件是操作系统级别的, ...
随机推荐
- 第23天:js-数据类型转换
一.padding1.内边距会影响盒子大小2.行内元素,尽量不用上下的padding和margin3.块元素嵌套块元素.子级会继承父级的宽度,高度由内容决定.如果给子级再设置padding,不会影响盒 ...
- Luogu1053 NOIP2005篝火晚会
首先造出所要求的得到的环.如果将位置一一对应上,答案就是不在所要求位置的人数.因为显然这是个下界,并且脑补一下能构造出方案达到这个下界. 剩下的问题是找到一种对应方案使错位数最少.可以暴力旋转这个环, ...
- bzoj 3132: 上帝造题的七分钟 (二维树状数组)
推推公式,最后变成四个东西的前缀和 然后不知道为什么一直wa,数据在本地测是没有错的& 好心的管理员还给了某位p党大神a了的代码,感人肺腑(虽然还是没发现到底我的程序是问题) var f1,f ...
- [国家集训队]最长双回文串 manacher
---题面--- 题解: 首先有一个直观的想法,如果我们可以求出对于位置i的最长后缀回文串和最长前缀回文串,那么我们枚举分界点然后合并前缀和后缀不就可以得到答案了么? 所以我们的目标就是求出这两个数列 ...
- 洛谷 CF1043F Make It One 解题报告
CF1043F Make It One 题意 从一堆数中选择最少的数,使它们的\(\gcd=1\) 输入输出格式 输入格式 第一行:一个正整数\(n\). 第二行:\(n\)个正整数,给出了这个数列. ...
- UVA.699 The Falling Leaves (二叉树 思维题)
UVA.699 The Falling Leaves (二叉树 思维题) 题意分析 理解题意花了好半天,其实就是求建完树后再一条竖线上的所有节点的权值之和,如果按照普通的建树然后在计算的方法,是不方便 ...
- 去掉vue 中的代码规范检测(Eslint验证)
去掉vue 中的代码规范检测(Eslint验证): 1.在搭建vue脚手架时提示是否启用eslint检测的. Use ESLint to lint your code? 写 no; 2.如果项目已经生 ...
- Java 匿名内部类 只能访问final变量的原因
文章来源:http://blog.sina.com.cn/s/blog_4b6f8d150100qni2.html 1)从程序设计语言的理论上:局部内部类(即:定义在方法中的内部类),由于本身就是在方 ...
- 【简单算法】22.删除链表的倒数第N个节点
题目: 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例: 给定一个链表: ->->->->, 和 n = . 当删除了倒数第二个节点后,链表变为 -& ...
- CentOS 6.5 下 QT4 连接 mysql 数据库的步骤
QT4 的安装请参考: CentOS 6.5 下安装 QT 4 mysql 的安装请参考: CentOS 6.5 下安装配置 mysql 1. 预防万一,先安装一下mysql-devel(一定要装!) ...