引
内存主要储存程序运行时所需的机器代码、数据等内容。内存是计算机运行的必要条件,所有计算机程序都必须要和内存打交道,如何使用内存是每个程序员所必须要处理好的问题。有效管理内存、有效使用内存是开发出高效、高可靠性、高安全性的程序的关键。本章将介绍 Windows 系统内存管理的原理、如何使用 Windows 系统为程序开发人员提供的内存管理 API 函数的来进行内存分配等操作,并对内存属性、权限等概念进行解释,讲解各种内存分配、释放、读写等操作的原理和方法。
数据是存储于内存中的,为了能够找到存在于内存中的数据,开发人员需要知道数据所在的内存地址。内存的最小存储单元是字节,内存中的每一个字节都有一个地址。在 32 位系统上,使用 32 位的数来表示内存地址,因此一共可以表示 232 (4294967296-4G)个字节。
基本概念
(1) 地址空间。
系统中所有可用的内存地址的集合称为地址空间。比如,如果可以使用 4GB 的内存,那么其地址空间就是 OxOOOOOOOO~OxFFFFFFF。
(2) 物理内存。
硬件系统中真实存在的存储空间称为物理内存, 物理内存的访问通过硬件系统总线进行的。
但不是每台 32 位的机器都具有 4GB 的物理地址空间,因此物理地址空间不一定是Ox00000000~OxFFFFFFFF,比如在物理内存为 1GB 的系统上,就可能只有 OxOOOOOOOO~Ox3FFFFFFF 的地址可以使用。
(3) 虚拟地址空间。
为了访问内存的统一和方便, 操作系统允许其上运行的程序访问所有的 4GB 内存空间中的地址。因此操作系统必须进行一些必要的地址转换工作,将程序访问的地址转换为物理内存中的真实物理地址,然后进行数据的存取。操作系统进行转换后,供程序使用的地址空间称为虚拟地址空间。在 32 位系统上,可以使用的虚拟地址空间大小是 4GB。当然,由于虚拟地址空间可能比真实物理地址空间大,系统会将部分虚拟地址空间中的地址转换为硬盘中的数据,在必要时将物理内存中的数据与硬盘中的数据进行交换。这种地址转换和数据交换是通过分页和分段机制实现的。
(4) 进程的内存空间:用户内存空间与内核内存空间。
Windows 操作系统中的每个进程都有属于自己的虚拟地址空间。32 位的 Window 操作系
统将 4GB(在 64 位系统上,这个值达到了 8TB)的虚拟内存划分为两个部分,进程使用 2GB,称为用户进程空间;内核使用 2GB,称为系统地址空间或内核地址空间(也可设置为用户进程空间使用 3GB,内核使用 1GB,这里只讨论各为 2GB 的情况),用户空间的地址范围为OxOOOOOOOO~Ox7FFFFFFF,内核空间的地址范围为 Ox80000000~OxFFFFFFFF。虚拟地址空间在进程上是封闭的,进程只能访问属于自己的地址空间,如果要访问其他进程的地址空间需特殊的机制。
堆与栈
内存管理中提到的堆与栈和数据结构中提到的不是一个概念,这点需要注意。这里提到的栈和堆是两种重要的内存管理形式。进程的每个线程都有自己的栈,栈与函数的调用、执行和返回及局部变量的保存相关,一般情况下, 栈中保存着函数的参数、返回地址和局部变量,调用函数将参数入栈,调用指令将函数返回地址入栈,被调用函数负责保存调用函数的相关栈指针,为局部变量分配空间等,而堆是一种可以动态分配和释放的内存,由堆管理器进行管理,用户在程序中使用的内存分配函数都是通过堆进行分配的。
用户使用内存分配函数分配的内存都位于堆中,所以使用堆管理函数对内存进行分配、释放等是最为直接的方式。Windows 系统中,每个进程都有自己的堆,每个进程的堆的数量也有所不同。Windows系统中所谓的“堆”(Heap)并不是内存块,而是一种用于内存管理的对象,也是一种内存组织的形式。进程可以从属于自己的堆上分配内存和释放内存。堆包括有若干种属性,如堆的大小最大值可以是固定的、也可以是“可增长的”;堆上的数据内容是否可以作为代码可执行;堆是否可连接存取等。
堆是一种内存管理的对象,一个进程有若干个堆,在分配内存前需要指定从哪个堆上进行分配。堆的句柄惟一标识了一个堆。在堆上分配内容前,首先需要获得所要进行分配的堆的句柄,获得堆的句柄有两种方式:一种是获取在进程中时已经创建好的堆,二是进程自己再创建堆。有了堆的句柄后,就可以在堆上进行内存分配了。
内存管理相关的API列表
堆管理API函数
API | 功能简介 |
---|---|
HeapCreate | 为进程创建堆,返回一个堆句柄 |
GetProcessHeap | 获取当前进程中的一个堆,返回一个句柄 |
GetProcessHeaps | 获取进程中的所有堆,包括堆的数量和各个堆的句柄 |
HeapAlloc | 从指定堆上分配内存块 |
GetSystemInfo | 获取系统信息 |
HeapFree | 释放分配的内存 |
HeapDestroy | 销毁堆 |
内存操作与内存管理API函数
API | 功能简介 |
---|---|
CopyMemory | 复制内存,第一个参数为目的地址,第二个参数为源地址,第三个参数为复制数据的大小,单位为字节 |
FillMemory | 填充内存,将一段内存填充为同一个值 |
MoveMemory | 和CopyMemory相似,不过源地址和目的地址可以相同 |
ZeroMemory | 将指定内存地址请零 |
GlobalMemoryStatusEX | 获取系统当前内存使用情况 |