Windows 内存的分配 收藏
Windows 使用一种
分页请求虚拟内存系统,现在我们就来分析一下这种系统。
虚拟地址空间
虚拟内存的概念在上个世纪五十年代就提出了,当时是作为解决不能一次装入实际内存的程序这一复杂问题的方案提出的。在虚拟内存系统中,程序可以访问超出可用物理内存的更大的地址集合,专用内存管理程序将这些逻辑地址映射到实际地址,使用磁盘上的临时存储保存超出的部分。
Windows 所使用的现代虚拟内存实现中,虚拟存储被组织成大小相同的单位,称为 页。每个操作系统进程占用自己的 虚拟地址空间,即一组可以读写的虚拟内存页。每个页可以有三种状态:
图 1. 进程地址空间中的虚拟页到物理页帧的映射
如果运行的是 32 位机器(如一般的 Intel 处理器),那么进程的整个虚拟地址空间就是 4GB,因为这是用 32 位所能寻址的最大地址空间。Windows 通常不会允许您访问地址空间中的所有这些内存,进程自己使用的只有不到一半,其他供 Windows 使用
虚拟内存的概念在上个世纪五十年代就提出了,当时是作为解决不能一次装入实际内存的程序这一复杂问题的方案提出的。在虚拟内存系统中,程序可以访问超出可用物理内存的更大的地址集合,专用内存管理程序将这些逻辑地址映射到实际地址,使用磁盘上的临时存储保存超出的部分。
Windows 所使用的现代虚拟内存实现中,虚拟存储被组织成大小相同的单位,称为 页。每个操作系统进程占用自己的 虚拟地址空间,即一组可以读写的虚拟内存页。每个页可以有三种状态:
- 自由:还没有进程使用这部分地址空间。如果企图访问这部分空间,无论读写都会造成某种运行时失效。该操作将导致弹出一个 Windows 对话框,提示出现了访问冲突。(Java 程序不会造成这种错误,只有用支持指针的语言编写的程序才可能造成这种问题。)
- 保留:这部分地址空间保留给进程,以供将来使用,但是在交付之前,不能访问该地址空间。很多 Java 堆在一开始处于保留状态。
- 提交:程序可以访问的内存,得到了完全 支持,就是说已经在分页文件中分配了页帧。提交的页只有在第一次被引用时才装入主存,因此成为 请求式分页。
图 1. 进程地址空间中的虚拟页到物理页帧的映射
如果运行的是 32 位机器(如一般的 Intel 处理器),那么进程的整个虚拟地址空间就是 4GB,因为这是用 32 位所能寻址的最大地址空间。Windows 通常不会允许您访问地址空间中的所有这些内存,进程自己使用的只有不到一半,其他供 Windows 使用
内存管理之二
Virtual Address Space
(虚拟地址空间)
虚拟地址表示的是进程中非实际物理地址的位置。换句话说就是,系统为每个进程维护着一个页面映射,这个页面映射是一个内部的数据结构,它的作用是将虚拟地址转换为相应的物理地址。
虚拟地址空间分为以下几部分:
Windows NT 4.0 SP3 Server Enterprise Edition, Windows 2000 Advanced Server, and Windows .NET Enterprise Server:一般的,进程能够使用低端的3GB空间(0x00000000 到0xBFFFFFFF)。高端的1GB内存(0xC0000000 到 0xFFFFFFFF)为系统保留部分。
Windows NT/2000/XP:进程能够使用低端的2GB内存(0x00000000 到 0x7FFFFFFF),高端的2GB内存(0x80000000 到 0xFFFFFFFF)系统保留。
Windows 95/98/Me:对应描述如下表
详情请参阅MSDN
虚拟地址表示的是进程中非实际物理地址的位置。换句话说就是,系统为每个进程维护着一个页面映射,这个页面映射是一个内部的数据结构,它的作用是将虚拟地址转换为相应的物理地址。
虚拟地址空间分为以下几部分:
Windows NT 4.0 SP3 Server Enterprise Edition, Windows 2000 Advanced Server, and Windows .NET Enterprise Server:一般的,进程能够使用低端的3GB空间(0x00000000 到0xBFFFFFFF)。高端的1GB内存(0xC0000000 到 0xFFFFFFFF)为系统保留部分。
Windows NT/2000/XP:进程能够使用低端的2GB内存(0x00000000 到 0x7FFFFFFF),高端的2GB内存(0x80000000 到 0xFFFFFFFF)系统保留。
Windows 95/98/Me:对应描述如下表
区间 | 用途 |
0K - ~64K (0xFFFF) | 不可写。这块区域大约应是为系统装载一些MS-DOS的特性而保留的。这块内存对于进程而言是私有的。 |
~64K (0x10000) - 4 MB (0x3FFFFF) | 为兼容MS-DOS保留。这块内存对进程而言可读可写。然而,这段区域有可能保存着一些MS-DOS相关的结构或代码,所以进程不应该在这段区域随意读出或写入。这块内存对于进程而言是私有的。 |
4MB (0x400000) - 2GB (0x7FFFFFFF) | 用于代码和用户数据。用户数据可读可写。代码是只执行的。这块内存对进程而言是私有的。 |
2GB (0x80000000) - 3GB (0xBFFFFFFF) | 共享区。对于所有进程都可读写。一些系统的动态连接库和其它一些数据装载到这段区域。 |
3GB (0xC0000000) - 4GB (0xFFFFFFFF) | 系统内存。对任意进程都是可读可写。然而,需要注意的是,这段内存是保存低等级的系统代码的地方,所以,向这片区域写入数据可能会破坏系统可能造成灾难性后果。. |
详情请参阅MSDN
- Virtual Address Space and Physical Storage Page State
- Scope of Allocated Memory
- 内存管理之三
Virtual Memory Functions( 虚拟内存函数族 )
此族函数用于进程操作或决定虚拟内存地址空间中页面的状态。他们可以实现以下一些操作:
1、 预留一个虚拟地址空间的区域。预留的地址空间不占用任何的实际物理内存,但是会防止其它分配操作在此范围内分配内存。它不会影响其它进程的虚拟地址空间。当进程需要把一个动态增长的数据结构分配在一个预留的内存空间中时,预留内存操作避免不必要的物理内存占用。进程在需要时能够为这块空间分配物理内存。
2、 将进程虚拟地址空间预留页的范围提交给进程,这样,这片物理存储区(不论是内存还是磁盘)只能被此进程访问。
3、 描述已提交页面区域的读/写、只读、拒绝访问等属性。不同于标准的分配函数的地方是它分配的页面总有 读/写 属性。
4、 释放已预留的内存区域。使这片虚拟内存区域对于调用进程后来的分配操作处于可用状态。
5、 把已提交页面区域解除提交。释放对应的物理存储区,使之对于后来的任意进程的分配操作有效。
6、 锁定一页或更多已提交页到物理内存中,使这些被锁定页面不回被系统交换到页面文件中。
7、 获得关于调用进程或指定进程虚拟地址空间某一区域之内已提交页面的资料。
8、 改变调用进程或指定进程虚拟地址空间指定区域已提交页面的访问限制级。内存管理之四
Allocating Virtual Memory (分配虚拟内存)
虚拟内存函数用于操作内存页面。这些函数用当前机器上的页面的大小来实现描述内存的大小和地址的功能。
VirtualAlloc 函数执行以下的操作中的一个:
1、 预留一页或多页的自由页面。
2、 提交一页或多页预留页面。
3、 预留和提交一页或多页自由页面。
你可以指定你要预留或提交页面的起始地址,或者你也可以让系统来决定这个地址。函数将指定的地址调整到适当的页面边界。预留页面是不允许访问的,但已提交的页面可以被以页读/写、页只读、页访问禁止等访问规则进行分配。当页面被提交之后,在页面文件中就分配了存储区,但所有页面都只是在第一次试图读或写该页面的时候被调入物理内存。你可以用一般指针来访问用VirtualAlloc函数提交的内存。
Freeing Virtual Memory (释放虚拟内存)
VirtualFree函数执行以下操作中的一个:
1、 取消提交一个或更多的已提交页面,把页面状态切换到预留。取消提交的页面将释放于该页面相关联的物理存储区,使这片区域能够被任意进程重新使用。所有被提交的页面块都可以被取消提交。
2、 释放一片包含一个或多个预留页面的内存块,把页面状态切换到空闲。已释放的页面块能够被该进程重新使用。被预留页面只能够一次释放全部的之前使用VirtualAlloc函数预留的块。
3、 同时取消提交和释放一个包含一个或多个页面的块,将这些页面的状态切换为空闲。指定的块必须包含全部之前使用VirtualAlloc函数预留的块,而且通常所有这些页面已经被提交。
当一个内存块被释放或者取消提交之后,你就不能再引用它。所有之前在其中的信息都永远消失。试图在一个空闲页面上执行读写操作会导致一个访问违例。如果你还需要这些某些信息,那么请不要取消提交或者释放包含这些资料的内存。
如果想要说明某片内存区域的数据不再游泳,那么在调用VirtualAlloc时使用MEM_RESET参数。这样这些页面上的数据将不会在读取或写入页面文件。然而,这块内存仍可再次被使用。
Working With Pages (页面操作)
使用GetSystemInfo函数能够得到当前机器的页面大小。
VirtualQuery 和VirtualQuerayEx函数能够返回关于 某一进程的地址空间中始于指定地址的连续的页面空间 的资料。VirtualQuery返回关于调用进程的内存资料。VirtualQueryEx返回关于指定进程的内存资料而且通常能够支持需要处于调试态进程资料的调试器。页面区域会被限制在 向下调整到最近页面边界的指定地址上。一直延伸到所有拥有以下共同特性的后续页面:
1、 所有页面状态一致:是已提交或预留或空闲三状态之一。
2、 如果起始页面非空闲,那么所有该区域页面都是被VirtualAlloc一起初始化的内存页面组的一部分。
3、 所有页面访问控制级都一致:是 页只读、页读写、页禁止访问三级之一。
VirtualLock函数使进程能够锁定一页或多页已提交内存进入物理内存(RAM),防止系统将这些页面交换到页面文件中。它能够被用于确保在不进行磁盘访问的情况下使临界数据可访问。锁定页面进内存是危险的,这是因为它会限制系统管理内存的能力。的使用VirtualLock函数能够导致可执行代码被交换到页面文件从而降低系统性能。VirtualUnlock函数将被VirtualLock锁定的内存解锁。
VirtualProtect函数能够调整所有在进程地址空间中的已提交页面的访问限制级。比如,进程可以分配读写页面来存储常变数据,并且之后它能够把访问控制级改变为只读或者禁止访问来防止以外的改写。VirtualProtect是一个用VirtualAlloc分配的页面上的典型的应用,但是,它也能够应用于使用其它分配函数分配的页面上。然而,VirtualProtect改变了全部页面的保护级,但是其它分配函数返回的指针并不一定要页面对齐。VirtualProtectEx函数类似于VirtualProtect函数,除了它只改变指定进程内存的限制级之外。改变限制级在调试器访问被调试进程的内存的时候会很有用处。