free() 是如何释放不同内存区块大小的指针?
最初是在知乎上看到这个问题的C++ delete[] 是如何知道数组大小的?,我也挺好奇,所以就作了一番工作。
申请内存时,指针所指向区块的大小这一信息,其实就记录在该指针的周围
看下面这段代码:
#include<cstdio>
#include<iostream>
#include<malloc.h>
#include<assert.h>
#include<ctime>
using namespace std; #define size 16 int main(void)
{
void * p = NULL;
srand(time());
int a = ;
while (a--)
{
int n = rand() % ;
p = malloc(n);
size_t w = *((size_t*)((char*)p - size));
cout << "w=" << w << endl;
cout << "n=" << n << endl;
assert(w == n);
free(p);
}
return ;
}
(注:如果是X86的CPU,请将 size 改为 8)
你会发现 w 和 n 始终是一致的,,这样其实不是巧合,来看 M$ 编译器 \ vc \include\ 目录下 malloc.h这一头文件 中 184 到 209 行的代码:
//这儿是根据不同的硬件平台的宏定义
#if defined (_M_IX86)
#define _ALLOCA_S_MARKER_SIZE 8
#elif defined (_M_X64)
#define _ALLOCA_S_MARKER_SIZE 16
#elif defined (_M_ARM)
#define _ALLOCA_S_MARKER_SIZE 8
#elif !defined (RC_INVOKED)
#error Unsupported target platform.
#endif /* !defined (RC_INVOKED) */ _STATIC_ASSERT(sizeof(unsigned int) <= _ALLOCA_S_MARKER_SIZE); #if !defined (__midl) && !defined (RC_INVOKED)
#pragma warning(push)
#pragma warning(disable:6540)
__inline void *_MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void *_Ptr, unsigned int _Marker)
{
if (_Ptr)
{
*((unsigned int*)_Ptr) = _Marker;
//
_Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
//最后返回给调用者的指针,是原始指针偏移了_ALLOCA_S_MARKER_SIZE的新指针,这也是刚才我将指针向后偏移,就能得到该指针所指向内存区块的大小的原因。
}
return _Ptr;
}
再来看看在 M$ 编译器中它是如何释放的,同样在 mallloc.h 文件249行到274行:
/* _freea must be in the header so that its allocator matches _malloca */
#if !defined (__midl) && !defined (RC_INVOKED)
#if !(defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC))
#undef _freea
__pragma(warning(push))
__pragma(warning(disable: ))
_CRTNOALIAS __inline void __CRTDECL _freea(_Pre_maybenull_ _Post_invalid_ void * _Memory)
{
unsigned int _Marker;
if (_Memory)
{
_Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;
//获得原始指针
_Marker = *(unsigned int *)_Memory;//得到指针所指区块的大小
if (_Marker == _ALLOCA_S_HEAP_MARKER)
{
free(_Memory);
}
#if defined (_ASSERTE)
else if (_Marker != _ALLOCA_S_STACK_MARKER)
{
#pragma warning(suppress: 4548) /* expression before comma has no effect */
_ASSERTE(("Corrupted pointer passed to _freea", ));
}
#endif /* defined (_ASSERTE) */
}
}
再来看看 SGI STL标准库源码 stl_alloc.h 文件209 行到 246行 debug_alloc类模板的设计:
// Allocator adaptor to check size arguments for debugging.
// Reports errors using assert. Checking can be disabled with
// NDEBUG, but it's far better to just use the underlying allocator
// instead when no checking is desired.
// There is some evidence that this can confuse Purify.
template <class _Alloc>
class debug_alloc { private: enum {_S_extra = }; // Size of space used to store size. Note
// that this must be large enough to preserve
// alignment. //这儿就像它所说的那样
public: static void* allocate(size_t __n)
{
//
这里实际申请的内存大小要多 个字节
char* __result = (char*)_Alloc::allocate(__n + (int) _S_extra);
*(size_t*)__result = __n;//前 4 个字节用于存储区块大小,可以看到,它预留了4个字节的空白区,具体原由 还望大牛能指出,==。
return __result + (int) _S_extra;//最后返回相对于原始指针偏移8个字节的新指针
} static void deallocate(void* __p, size_t __n)
{
char* __real_p = (char*)__p - (int) _S_extra;//获得原始指针
assert(*(size_t*)__real_p == __n);//这里增加了一个断言,防止析构了被破坏的指针
_Alloc::deallocate(__real_p, __n + (int) _S_extra);
} static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz)
{
char* __real_p = (char*)__p - (int) _S_extra;
assert(*(size_t*)__real_p == __old_sz);
char* __result = (char*)
_Alloc::reallocate(__real_p, __old_sz + (int) _S_extra,
__new_sz + (int) _S_extra);
*(size_t*)__result = __new_sz;
return __result + (int) _S_extra;
} };
再来看看 gcc 下,其实也有类似的设计:
#if(defined(_X86_) && !defined(__x86_64))
#define _ALLOCA_S_MARKER_SIZE 8
#elif defined(__ia64__) || defined(__x86_64)
#define _ALLOCA_S_MARKER_SIZE 16
#endif #if !defined(RC_INVOKED)
static __inline void *_MarkAllocaS(void *_Ptr,unsigned int _Marker) {
if(_Ptr) {
*((unsigned int*)_Ptr) = _Marker;
_Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
}
return _Ptr;
}
#endif
#ifndef RC_INVOKED
#undef _freea
static __inline void __cdecl _freea(void *_Memory) {
unsigned int _Marker;
if(_Memory) {
_Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;
_Marker = *(unsigned int *)_Memory;
if(_Marker==_ALLOCA_S_HEAP_MARKER) {
free(_Memory);
}
#ifdef _ASSERTE
else if(_Marker!=_ALLOCA_S_STACK_MARKER) {
_ASSERTE(("Corrupted pointer passed to _freea",));
}
#endif
}
}
#endif /* RC_INVOKED */
其实,很多在实际写代码中困惑我们的问题,都可以通过 阅读相关源代码来得到 答案。
所以,经常阅读那些开源代码,还是相当有好处的 :)
free() 是如何释放不同内存区块大小的指针?的更多相关文章
- 用 free 或 delete 释放了内存之后,立即将指针设置为 NULL,防止产 生“野指针”
用 free 或 delete 释放了内存之后,立即将指针设置为 NULL,防止产 生“野指针”. #include <iostream> using namespace std; /* ...
- C语言中free函数是如何确定要释放多少内存空间的
本文链接:http://www.cnblogs.com/xxNote/p/4009359.html 今天看书的时候看到free函数释放动态申请的内存时只需要把内存块的首地址传过去就行了,显然仅仅依靠首 ...
- 手工释放linux内存——/proc/sys/vm/drop_caches
--手工释放linux内存——/proc/sys/vm/drop_caches 总有很多朋友对于Linux的内存管理有疑问,之前一篇日志似乎也没能清除大家的疑虑.而在新版核心中,似乎对这个问题提供了新 ...
- 如何计算Java对象所占内存的大小
[ 简单总结: 随便一个java项目,引入jar包: lucene-core-4.0.0.jar 如果是 maven项目,直接用如下依赖: <dependency> <groupId ...
- linux如何手动释放linux内存
当在Linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching.这个问题,貌似有不少人在问,不过都没有看到有什么很好解决的办法.那么我来谈谈这个问题 ...
- Android中App可分配内存的大小
现在真实测试结果: 1,为了搞清楚每个应用程序在Android系统中最多可分配多少内存空间,我们使用了真机进行测试,测试机型为魅族MX4 Pro,3G内存. 测试方法是直接申请一块较大的内存空间,看应 ...
- free()后内存不释放问题 - 内存缓冲池技术(转)
起因 下面这段代码执行后,内存有增无减,增加了200M,iOS平台200M不能接受了 // STL 集合类 void test1() { list<int> mList; for (int ...
- Android中App可分配内存的大小(转)
转自:http://blog.csdn.net/u011506413/article/details/50965435 现在真实测试结果: 1,为了搞清楚每个应用程序在Android系统中最多可分配多 ...
- 释放linux内存中的cache缓存
echo 3 > /proc/sys/vm/drop_caches 记一次 经常用 exp 导出oracle全量数据库,发现linux内存一直在减小没有释放,即使 oracle重启也不行,只有 ...
随机推荐
- SLua 中使用 Lua 5.3 的编译工程
2016-03-05 更新: 之前编译的库,在 Android 下 Lua_Number 和 Lua_Integer 被编译为了32位,导致从 C# 到 Lua 过程中有64位到32位整型转换会出现溢 ...
- JavaScript高级程序设计29.pdf
insertAdjacentHTML方法 插入标记最后一个新增的方式是insertAdjacentHTML()方法,它接收两个参数:插入位置和要插入的HTML文本,第一个参数必须是下列值之一: &qu ...
- Linux,实时获取磁盘空间
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <sys/statfs ...
- windows下redis的安装配置和php扩展使用phpredis
1. 首先安装先下载redis数据库 下载地址: http://code.google.com/p/servicestack/wiki/RedisWindowsDownload 目前是2.02 ...
- JQuery- 动画与效果
这几天做网站,刚好用到! 1.基本效果 匹配元素从左上角开始变浓变大或缩小到左上角变淡变小 ①隐藏元素 除了可以设置匹配元素的display:none外,可以用以下函数 hide(speed,[cal ...
- UVA Mapping the Swaps
题目例如以下: Mapping the Swaps Sorting an array can be done by swapping certain pairs of adjacent entrie ...
- BringWindowToTop(), SetForegroundWindow(), SetActiveWindow()
1. SetActiveWindow() 原型: <span style="font-size:14px;">CWnd* SetActiveWindow(); HWND ...
- Tomcat优化参数
JAVA_OPTS="-Xmx400m -Xms400m -XX:PermSize=128m -XX:MaxPermSize=400m -XX:+UseG1GC -XX:MaxGCPause ...
- tcpreplay安装使用
#Author: ypguo#Data: 2010.4.23#Version: 1.2 增加了修改VLAN tag内容. 1.1 修改了cygwin下安装的内容 ...
- 【NodeJs】使用TCP套接字收发数据的简单实例
因为TCP协议是流协议,在收发数据的时候会有粘包的问题.本例使用自定义的SPtcp封包协议对TCP数据再进行一次封装,解决了粘包问题. 注:其性能仍有待优化.优化方向:使用TCP自带的接收窗口缓存. ...