Windows 虚拟内存理解

这篇文章写于2015年,之前仅作为私人文档,现在公开如下。

1. 问题

Windows 32位系统中,虚拟内存地址空间可以达到4GB,且每个进程都可独享虚拟内存,其中内核代码可以访问整个4GB虚拟地址空间,用户态只能访问2GB虚拟地址。

问题:如果一个台PC中,内存很小,例如只有1GB,如果有多个用户进程都想访问到2GB内存空间,即多个进程想访问的内存空间的总和远超过1GB的内存空间,这如何实现?

2. 猜想

《Windows内核原理与实现》一书并没有直接回答该问题。

《Windows内核原理与实现》一书说到(page177),“一般的做法是把不紧急的进程中的数据或代码先放到外存(通常是硬盘)中,从而把它们占用的物理内存腾出给紧急的进程使用”,但是这是阐释依然没有解答问题,这个阐释只回答了一点,而不够全面。问题中进程想访问的内存空间远超过物理内存,即使其他进程腾出物理内存给紧急进程,依然不够用,那么如何处理呢?

查看各方面的资料,理解如下:

(1)申请不等于分配

Windows采取按需分配的策略,只有当一段虚拟内存被真正使用时,系统才会给他分配页表和物理页面。一个进程某个变量申请了一段内存,如果某个地址的内存空间还未被真正使用,操作系统不会为它分配页表和物理页面。

(2)向硬盘扩充空间作为虚拟内存

所谓的虚拟内存,即并非是真正的物理内存,其部分虚拟内存空间位于硬盘上,称为页交换文件。当操作系统的物理内存不够用,一个进程需要分配的内存空间较大时,会从硬盘分配一段空间作为虚拟内存的一部分,从而扩充为2GB虚拟内存空间。

(3)其他进程腾出物理内存

即《Windows》一书说到的,其他进程(优先级低或处于睡眠状态)的物理内存空间被操作系统回收,将该进程的所有数据与信息转移到硬盘上,从而腾出物理内存给紧缺的进程使用。

(4)同一进程腾出物理内存

一个进程不太可能同一时刻都使用完整的2GB内存空间,可能只是同时使用一段或多段内存空间,这样同一进程中不用到的数据可以转移到硬盘,从而在同一进程中腾出物理内存空间给本进程中需要访问和处理的数据使用。

但是,进程只能访问物理内存中的页面,如果进程访问到一个不在物理内存中的页面时,产生一个缺页中断,进程被锁定,操作系统把硬盘中的页面中的数据转入到内存,一旦页面转入内存,进程被解锁,可以重新运行。当然进程并不知道这个锁定和解锁的过程。

扫描二维码关注公众号,回复: 9658022 查看本文章

3. 实验

PC环境:Win7 64位,总共12GB内存,程序运行前,内存已被占用约7GB。Visual Studio 2015 IDE环境

(1)直接一次申请大内存

实验A: 32位进程 Debug 模式下直接一次申请1GB内存

p的值有效,Debug模式下可以1次申请到1GB内存,申请后内存升到了8GB。

 

 

实验B: 32位进程 Debug模式 1次申请2GB内存,直接就申请失败

以上2GB的申请程序,改为Release模式,也是申请失败。

 

实验C:64位进程一次性申请2GB

64位进程,无论是Debug还是Release模式,一次性申请2GB内存,都失败了。

 

(2)分批次申请内存

实验D: 32位进程申请2GB内存,每次申请2MB,尝试申请1024次

我这里仅用于测试,没有写释放p[i]内存的操作,但结束调试或结束进程后,内存其实是有被操作系统回收掉的。

在第933次申请时出现异常,即成功申请了932次,即总共成功申请了932 * 2MB = 1864MB内存

上面相邻两个内存地址的差值为0x210000, 即十进制的2162688,即2112KB,比2048KB多64KB,这个64KB是哪里来的?这个64KB是编译器在Debug模式下加入的,用于记录Debug模式下的一些信息。

进程内存增长曲线如下:

(Debug模式)

 

实验E: 分批次申请,每次申请10MB

下面测试了32位进程 每次申请10MB, 相邻两个内存地址的差值为0xA10000, 即十进制的10304KB,比10240KB多64KB

如果用malloc来分配,下面内存间隔也是0x210000,即比2MB,多64KB,当malloc失败时,返回NULL

64位进程:

如果编译为64位进程,则可申请到2GB的内存空间,如下:

从以上实验,我们可以得到Windows下Debug模式与Release模式的区别

Debug模式下,申请内存时会真正给进程分配内存,而Release模式下,申请内存后,操作系统并没有真正给进程分配内存,而只是提供了一个内存地址,如果不对该地址进行写操作,该进程实际上并没有实际占用这些内存空间。

例如,Release模式下,进程运行期间,任务管理器的内存并没有递增

 

64位 release模式下,可以申请到全部10GB内存空间(物理内存为12GB),如下,每次申请10MB,每次都申请成功,但任务管理器中的内存线并没有变化。

 

 

32位进程,不管是release还是debug,都无法申请到2GB内存空间,用malloc分配,分配到接近2GB时,返回NULL指针。

64位进程,release模式,每次申请100MB,内存地址相邻的间隔刚好100MB, 记录如下,大约申请到了40多GB的内存空间

此时系统开始出现一些异常情况,切换到其他程序时,其他程序反应慢,且程序窗口黑屏

 

实验Z:

64位进程,Releae模式下执行以下代码,申请10GB内存(系统总共12GB物理内存,已经占用7GB),使用了Zeromemory对内存进行了写入操作。

实现过程中系统卡住,仿佛死机。继续等待约5分钟后,弹出窗口提示程序运行出错,程序退出。程序退出后,系统的内存占用只有3.5GB内存。应该是程序运行过冲中,系统检测到内存不够用,将其他不在使用中的进程的物理内存释放,数据转到硬盘上的页交换文件上,从而腾出更多的物理内存空间。

 

 

 

实验结论:

  1. 32位进程实际上无法申请超过2GB内存(无论是Debug,还是Release,无论是一次性还是分批次)。
  2. 32位进程与64位进程,一次性申请的内存有限制(实验中申请1GB成功,2GB失败)。
  3. Debug模式下进程申请到的内存事实上都被该进程占有,而在Release模式下,如果不对申请到的内存做写操作,进程仅记录内存的地址,并不占有该内存空间,直到对内存进行读写操作才实际占有该地址的内存空间。
  4. 一个进程累计要申请的内存空间(伴随写入操作),大于剩下的物理内存空间。操作系统首先将空闲的物理内存分配给该进程,然后将系统中的非活动的进程的部分物理内存释放,将其写入到页交换文件中,腾出一部分物理内存空间给要当前进程。

 

发布了40 篇原创文章 · 获赞 51 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/zh8706/article/details/100396503