关于windows下堆内存的申请与释放
2011-12-14 09:33
337 查看
版权所有,转载请注明出处,谢谢!
http://blog.csdn.net/walkinginthewind/article/details/7069176
我们都知道,C语言中要动态申请内存需要调用malloc函数,释放动态内存需要调用free函数。内存的申请与释放都是在堆(Heap)上进行的。当然,所谓的内存,都是虚拟内存。
C语言中的malloc和free,在windows中主要是通过HeapAlloc和HeapFree来实现的。
每个进程在初始化的时候,会调用RtlProcessHeap()函数构造进程的HEAP对象,这个对象用来管理进程的堆内存。
当我们使用malloc申请一段内存时,我们要指定大小,但是使用free释放的时候,只是指定要释放的内存起始地址即可。
如:
int * p = (int*)malloc(100 * sizeof(int)); // 申请100个int大小的一段内存
... // 其他操作
free(p); // 释放p所指向的内存
那么我们必定会产生疑问,系统是怎么知道或记录给定指针所指向的动态内存的大小的呢?
windows的实现方案很简单,就是在每一段动态内存的上部保存该段内存的大小等相关信息,这也就说明了,当我们使用堆内存时,会有额外的系统开销,windows中是通过如下一个结构体来保存相关信息的:
(WRK中的定义,在XP和win7下测试符合,win2000源码中的定义与此不同)
因为free释放内存的时候要依赖于该结构体中的信息,所以对于不是malloc返回的地址或已被释放过的地址,该结构的内容一般是不正确的,这也就是free会出错的原因。
如:
int * p = (int*)malloc(100 * sizeof(int)); // 申请100个int大小的一段内存
... // 其他操作
free(p); // 释放p所指向的内存
free(p); // 再次释放已释放的内存,会出错,因为RTL_HEAP_ENTRY已经不再有效。
所以当我们释放指针所指的动态内存后,我们最好将指针赋值为NULL,因为free一个值为NULL的地址,不会有任何错误。
再如:
int * p = (int*)malloc(100 * sizeof(int)); // 申请100个int大小的一段内存
... // 其他操作
//比如我们想释放部分内存
int * q = p + 20;
free(q); // 释放q所指向的内存,会出错,因为地址q所对应的RTL_HEAP_ENTRY结构体信息是无效的
所以free只能释放malloc返回的有效地址。
下面通过一个程序验证一下:
输出结果是:
i: 0, size: 0
i: 1, size: 1
i: 2, size: 2
...
i: 999, size: 999
总结:本文根据windows源码简单的分析了windows对C库函数free的实现中对内存大小信息的记录方法。
http://blog.csdn.net/walkinginthewind/article/details/7069176
我们都知道,C语言中要动态申请内存需要调用malloc函数,释放动态内存需要调用free函数。内存的申请与释放都是在堆(Heap)上进行的。当然,所谓的内存,都是虚拟内存。
C语言中的malloc和free,在windows中主要是通过HeapAlloc和HeapFree来实现的。
每个进程在初始化的时候,会调用RtlProcessHeap()函数构造进程的HEAP对象,这个对象用来管理进程的堆内存。
当我们使用malloc申请一段内存时,我们要指定大小,但是使用free释放的时候,只是指定要释放的内存起始地址即可。
如:
int * p = (int*)malloc(100 * sizeof(int)); // 申请100个int大小的一段内存
... // 其他操作
free(p); // 释放p所指向的内存
那么我们必定会产生疑问,系统是怎么知道或记录给定指针所指向的动态内存的大小的呢?
windows的实现方案很简单,就是在每一段动态内存的上部保存该段内存的大小等相关信息,这也就说明了,当我们使用堆内存时,会有额外的系统开销,windows中是通过如下一个结构体来保存相关信息的:
(WRK中的定义,在XP和win7下测试符合,win2000源码中的定义与此不同)
typedef struct _RTL_HEAP_ENTRY { SIZE_T Size; // 指示该段内存的大小 USHORT Flags; USHORT AllocatorBackTraceIndex; union { struct { SIZE_T Settable; ULONG Tag; } s1; // All other heap entries struct { SIZE_T CommittedSize; PVOID FirstBlock; } s2; // RTL_SEGMENT } u; } RTL_HEAP_ENTRY, *PRTL_HEAP_ENTRY;
因为free释放内存的时候要依赖于该结构体中的信息,所以对于不是malloc返回的地址或已被释放过的地址,该结构的内容一般是不正确的,这也就是free会出错的原因。
如:
int * p = (int*)malloc(100 * sizeof(int)); // 申请100个int大小的一段内存
... // 其他操作
free(p); // 释放p所指向的内存
free(p); // 再次释放已释放的内存,会出错,因为RTL_HEAP_ENTRY已经不再有效。
所以当我们释放指针所指的动态内存后,我们最好将指针赋值为NULL,因为free一个值为NULL的地址,不会有任何错误。
再如:
int * p = (int*)malloc(100 * sizeof(int)); // 申请100个int大小的一段内存
... // 其他操作
//比如我们想释放部分内存
int * q = p + 20;
free(q); // 释放q所指向的内存,会出错,因为地址q所对应的RTL_HEAP_ENTRY结构体信息是无效的
所以free只能释放malloc返回的有效地址。
下面通过一个程序验证一下:
#include<stdio.h> #include<windows.h> typedef struct _RTL_HEAP_ENTRY { SIZE_T Size; USHORT Flags; USHORT AllocatorBackTraceIndex; union { struct { SIZE_T Settable; ULONG Tag; } s1; struct { SIZE_T CommittedSize; PVOID FirstBlock; } s2; } u; } RTL_HEAP_ENTRY, *PRTL_HEAP_ENTRY; int main() { PRTL_HEAP_ENTRY pHeapEntry; int *p; for(int i = 0; i < 1000; i++) { p=(int*)malloc(i); pHeapEntry=(PRTL_HEAP_ENTRY(p)-1); printf("i: %d, size: %d\n", i, pHeapEntry->Size); free(p); } return 0; }
输出结果是:
i: 0, size: 0
i: 1, size: 1
i: 2, size: 2
...
i: 999, size: 999
总结:本文根据windows源码简单的分析了windows对C库函数free的实现中对内存大小信息的记录方法。
相关文章推荐
- Student Manager--关于内存申请和释放
- 关于在dll中申请内存,外部释放的问题
- 有关于malloc申请内存和free内存释放
- 7.关于申请与释放内存new &amp; delete
- 关于在dll中申请内存,外部释放的问题
- 关于什么时候使用CoTaskMemAlloc/CoTaskMemFree来申请/释放内存
- 关于库函数里申请的内存空间在库函数外释放的问题的一些总结
- 关于在dll中申请内存,外部释放的问题
- 关于QT的内存申请和释放
- 关于在dll中申请内存,外部释放的问题
- 关于Cross-Dll问题(在不同的模块之间申请和释放内存)
- 关于QT的内存申请和释放
- 指针申请内存和释放
- 关于free如何知道要释放内存空间的长度问题(ARM篇)
- linux驱动开发--内核空间中内存的申请与释放
- 堆内存申请与释放的标准流程
- 关于free如何知道要释放内存空间的长度问题
- MTK内存动态申请释放
- 监控应用程序的内存申请和释放
- 关于Windows内存结构