有半个多月没有写博客了,应该是工作和生活比较忙吧。技术积累应该是不断的,今天就整理一下window的内存管理相关的知识吧。最近公司的其他同事分享了关于windows的内存管理的一些内在机制,主要集中在堆的分配和管理,这些知识点主要是侧重windows内核的。当时就觉得自己对windows的内存管理这块儿不是很熟,虽然大概的知识都了解,包括堆、栈等,但是由于工作中用到的不多,实际的工作中不会去自己写一些底层的内存分配和释放机制。我们工作是用C++进行开发,分配内存的途径就是new 和malloc两种方法,很少会用到win32的底层函数heapcreate,virtualCreate等。看一些开源的代码,比如v8和stl,他的里面就是自己进行heap的创建等。为什么c++已经提供了new函数,这些开源的代码还要自己管理复杂的内存分配呢?回过头来又将window核心编程里的内存管理这一部分看了一遍,记录学到的东西于此。
window中提供了三种进行内存操控的机制:
上面的每种机制都说明了其适用的场合。
进程在启动的时候,windows会在进程的地址空间中创建一个默认的堆,这个默认堆是一些windows函数进行内存分配的地方。
我们一般使用的new所分配的内存都存在于C运行时内建的堆中,这一点我们可以通过char *c = new char这个语句进行单步调试,就可以看到最终的内存分配位于malloc.c中的代码,从crt的默认堆中进行heapalloc的操作来分配内存,这个堆是在c运行时初始化时进行创建的。而v8中的ast节点的对象都分配在自己创建的堆上。这两个例子都是堆内存管理的典型应用场景,进行大量的小型对象的内存管理。那么用堆有哪些具体的优点呢?
知道了堆的这些好处,就可以用win32api来进行堆的使用了。
HANDLE WINAPI HeapCreate( __in DWORD flOptions, __in SIZE_T dwInitialSize, __in SIZE_T dwMaximumSize);
这个函数进行堆的创建, flOptions参数可选的参数如下:HEAP_CREATE_ENABLE_EXECUTE: 堆中的代码可以执行HEAP_NO_SERIALIZE: 堆不需要 顺序访问,如果只有单一的线程访问此堆,通过这个选项可以提高性能。dwInitialSize 参数: 初始大小。 dwMaximumSize 堆的最大尺寸,如果未指定,则紧着计算机内存增长。
LPVOID WINAPI HeapAlloc( __in HANDLE hHeap, __in DWORD dwFlags, __in SIZE_T dwBytes);从堆上分配内存。
BOOL WINAPI HeapFree( __in HANDLE hHeap, __in DWORD dwFlags, __in LPVOID lpMem);释放一块在对堆上分配的内存。
BOOL WINAPI HeapDestroy( __in HANDLE hHeap);销毁一个堆。
我自己对堆的理解是操作系统为我们在虚拟内存的基础之上做了一层封装,方便我们的使用。当分配一个堆时,对应的后台有地址空间的保留和物理内存的提交,通过操作系统为我们所做的工作,可以比较简单的进行堆内存的管理,不需要对后台的虚拟内存很清楚也可以进行。
在C++中进行堆的使用时需要进行对象的new操作符的重载,将对象分配在指定的堆上。
这部分的内容较多,不会仔细的进行描述,大概记录一下。
window下的每个进程都有自己独立的内存地址空间,对于32位的系统来说,内存空间大小为4GB, 低2GB的空间的应用程序的地址空间,高2GB的空间是操作系统的内核运行的空间。当系统创建一个进程时,地址空间的大部分区域都是free的,为了使用这部分,我们需要用VirtualAlloc来进行地址空间的预订和物理内存的调拨。当进行地址空间预订时,系统会确保所分配的区域起始地址是系统分配粒度的整数倍,而区域大小则是系统页面大小的整数倍。我们可以给每个分配的页面指定不同的页面保护属性,比如:PAGE_NOACCESS, PAGE_EXECUTE, PAGE_READWRITE等符号,来限制在这一个内存区域上所能进行的操作类型。
虚拟内存的函数主要有:VirtualAlloc, VirtualFree, VirtualProtect, VirtualQuery 等等。