> 文档中心 > 在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

早上看到读者在群里讨论这些面试题:

在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

其中,第一个问题「在 4GB 物理内存的机器上,申请 8G 内存会怎么样?」存在比较大的争议,有人说会申请失败,有的人说可以申请成功。

这个问题在没有前置条件下,就说出答案就是耍流氓。因为在 32 位操作系统和 64 位操作系统场景下,答案是不同的。

另外,我们还要看申请完 8G 内存后会不会被使用,会被使用是一种情况,不会被使用又是另外一种情况了。

所以,我们要分场景讨论。

正文

应用程序通过 malloc 函数申请内存的时候,实际上申请的是虚拟内存,此时并不会分配物理内存。

当应用程序读写了这块虚拟内存,CPU 就会去访问这个虚拟内存, 这时会发现这个虚拟内存没有映射到物理内存, CPU 就会产生缺页中断,进程会从用户态切换到内核态,并将缺页中断交给内核的 Page Fault Handler (缺页中断函数)处理。

缺页中断处理函数会看是否有空闲的物理内存:

  • 如果有,就直接分配物理内存,并建立虚拟内存与物理内存之间的映射关系。
  • 如果没有空闲的物理内存,那么内核就会开始进行回收内存的工作,如果回收内存工作结束后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会放最后的大招了触发 OOM (Out of Memory)机制。

32 位操作系统和 64 位操作系统的虚拟地址空间大小是不同的,在 Linux 操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分,如下所示:

在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

通过这里可以看出:

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

现在可以回答这个问题了:在 32 位操作系统、4GB 物理内存的机器上,申请 8GB 内存,会怎么样?

因为 32 位操作系统,进程最多只能申请 3 GB 大小的虚拟内存空间,所以进程申请 8GB 内存,在申请虚拟内存阶段就会失败(我手上没有 32 位操作系统测试,我估计失败的原因是 OOM)。

在 64 位操作系统、4GB 物理内存的机器上,申请 8G 内存,会怎么样?

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

我们可以简单做个测试,我的服务器是 64 位操作系统,但是物理内存只有 2 GB。

在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

现在,我在机器上,申请 4 GB 内存,注意下面代码只是单纯分配了虚拟内存,并没有使用该虚拟内存:

#include #include #include #include #include int main() {    int ret;    char* addr[4];    printf("使用cat /proc/%d/maps查看内存分配\n",getpid());    size_t s = 1024 * 1024 * 1024;    int i = 0;    for(i = 0; i < 4; ++i) { printf("alloc size = %d\n", s); addr[i] = (char*) malloc(s); printf("主线程调用malloc后,申请1gb大小得内存,此内存起始地址:0X%x\n", addr[i]);    }    getchar();    return 0;}

然后运行这个代码,可以看到,我的物理内存虽然只有 2GB,但是程序正常分配了 4GB 大小的虚拟内存

在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

我们可以通过下面这条命令查看进程的虚拟内存大小:

# ps aux | grep alloc_4gUSERPID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMANDroot      7797  0.0  0.0 4198540  352 pts/1    S+   16:58   0:00 ./alloc_4g

其中,VSZ 就代表进程使用的虚拟内存大小,RSS 代表进程使用的物理内存大小。可以看到,VSZ 大小为 4198540,也就是 4GB 的虚拟内存。

然后,我们改一下代码,在申请完虚拟内存后,通过 memset 函数使用这个虚拟内存,看看会发生什么。

#include #include #include #include #include int main() {    int ret;    char* addr[4];    printf("使用cat /proc/%d/maps查看内存分配\n",getpid());    size_t s = 1024 * 1024 * 1024;    int i = 0;    for(i = 0; i < 4; ++i) { printf("alloc size = %d\n", s); addr[i] = (char*) malloc(s); printf("主线程调用malloc后,申请1gb大小得内存,此内存起始地址:0X%x\n", addr[i]); //访问虚拟内存 memset(addr[i], 0, s);    }    getchar();    return 0;}

运行结果:

在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

可以看到,在申请了 2GB 虚拟内存后,然后马上使用了这块虚拟内存,由于这台机器的物理内存只有 2 GB,所以发生了 OOM

至此, 验证完成了。简单总结下:

  • 在 32 位操作系统,因为进程最大只能申请 3 GB 大小的虚拟内存,所以直接申请 8G 内存,会申请失败。
  • 在 64位 位操作系统,因为进程最大只能申请 128 TB 大小的虚拟内存,即使物理内存只有 4GB,申请 8G 内存也是没问题,因为申请的内存是虚拟内存,等这块虚拟内存被访问了,因为物理空间不够,就会发生 OOM。