虚拟内存与Swap机制探究

现代操作系统几乎都采用了虚拟内存机制,从一个常见的问题引出:在 4GB 物理内存的机器上,申请 8G 内存会怎么样呢?回答这个问题是需要具体情况分别看待的,来看看不同的机器、不同的Swap机制会有怎样的答案。

虚拟内存分布

对于32位操作系统,32根地址线最大能访问到的内存只有4GB,其内核空间占用1G,位于最高处,3G用户空间;

对于64位操作系统,内核空间和用户空间都是128T,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的;

根据这个内存分布情况,如果直接在32位操作系统申请8GB内存,那么会直接报错,因为进程之只能申请到3GB的空间,所以在申请阶段就会失败。所以不管有没有开启Swap机制,申请内存大小超过3GB就会报错。

所以对于32位操作系统,申请 8G 内存会直接报错!

申请内存流程

在Linux上申请内存时,申请到的是虚拟内存,此时还并未分配对应的物理内存,只有这块内存发生了读写操作的时候,会发现这个虚拟内存没有映射到物理内存, CPU 就会产生缺页中断,进程会从用户态切换到内核态,并将缺页中断交给内核的缺页中断函数来处理:如果有就直接分配物理内存,并在页表建立映射关系;如果不够就会触发GC(此处的GC是指内核进行的内存垃圾回收),然后如果有就直接分配物理内存,没有则发生OOM!

64位系统情况

那么现在来看看64位操作系统的情况:

对于64位操作系统,进程可以使用 128 TB 大小的虚拟内存空间,所以进程申请 8GB 内存是没问题的,因为进程申请内存是申请虚拟内存,只要不读写这个虚拟内存,操作系统就不会分配物理内存。

 1#include <stdio.h>
 2#include <stdlib.h>
 3
 4int main(){
 5	for(int i = 0; i < 4; i++){
 6	    malloc(1024 * 1024 * 1024);
 7	}
 8	while(1);
 9	return 0;
10}

开始编译、执行:

1gcc demo.c -o test -std=c99
2    
3./test
4    
5ps -aux|grep test

其中VSZ 就代表进程使用的虚拟内存大小,RSS 代表进程使用的物理内存大小,可以看出虚拟内存申请了4GB,物理内存使用了348字节!

我的云服务器内存大小是2GB,可见申请4GB内存也是OK的!

Swap机制

当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间会被临时保存到磁盘,等到那些程序要运行时,再从磁盘中恢复保存的数据到内存中。

Swap 就是把一块磁盘空间或者本地文件,当成内存来使用,它包含换出和换入两个过程:

  • 换出(Swap Out),是把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存;
  • 换入(Swap In),是在进程再次访问这些内存的时候,把它们从磁盘读到内存中来;

Linux 中的 Swap 机制会在内存不足和内存闲置的场景下触发,kswapd 是 Linux 负责页面置换的守护进程,它也是负责交换闲置内存的主要进程:

最终结论

1、在 32 位操作系统,因为进程最大只能申请 3 GB 大小的虚拟内存,所以直接申请 8G 内存,会申请失败。

2、在 64位 位操作系统,因为进程最大只能申请 128 TB 大小的虚拟内存,即使物理内存只有 4GB,申请 8G 内存也是没问题,因为申请的内存是虚拟内存。但是如果这块虚拟内存被访问了,要看系统有没有 swap 分区:

  • 没有 swap 分区:那就看物理内存够不够大了,不够的话会OOM
  • 有 swap 分区:要看swap分区有多大,如果实际使用中如果swap分区也不够用了,也会OOM