当前位置:网站首页>问题来了:4GB物理内存的机器上申请8G内存能成功吗?
问题来了:4GB物理内存的机器上申请8G内存能成功吗?
2022-08-09 11:44:00 【Park33448】
一大早看见群里的人在讨论面试题
其中,第一个问题「在 4GB 物理内存的机器上,申请 8G 内存会怎么样?」存在比较大的争议,有人说会申请失败,有的人说可以申请成功。
这个问题在没有前置条件下,就说出答案就是耍流氓。因为在 32 位操作系统和 64 位操作系统场景下,答案是不同的。
另外,我们还要看申请完 8G 内存后会不会被使用,会被使用是一种情况,不会被使用又是另外一种情况了。
所以,我们要分场景讨论。
应用程序通过 malloc 函数申请内存的时候,实际上申请的是虚拟内存,此时并不会分配物理内存。
当应用程序读写了这块虚拟内存,CPU 就会去访问这个虚拟内存, 这时会发现这个虚拟内存没有映射到物理内存, CPU 就会产生缺页中断,进程会从用户态切换到内核态,并将缺页中断交给内核的 Page Fault Handler (缺页中断函数)处理。
缺页中断处理函数会看是否有空闲的物理内存:
如果有,就直接分配物理内存,并建立虚拟内存与物理内存之间的映射关系。
如果没有空闲的物理内存,那么内核就会开始进行回收内存的工作,如果回收内存工作结束后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会放最后的大招了触发 OOM (Out of Memory)机制。
32 位操作系统和 64 位操作系统的虚拟地址空间大小是不同的,在 Linux 操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分,如下所示:
通过这里可以看出:
32 位系统的内核空间占用 1G,位于最高处,剩下的 3G 是用户空间;
64 位系统的内核空间和用户空间都是 128T,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的。
现在可以回答这个问题了:在 32 位操作系统、4GB 物理内存的机器上,申请 8GB 内存,会怎么样?
因为 32 位操作系统,进程最多只能申请 3 GB 大小的虚拟内存空间,所以进程申请 8GB 内存,在申请虚拟内存阶段就会失败(我手上没有 32 位操作系统测试,我估计失败的原因是 OOM)。
在 64 位操作系统、4GB 物理内存的机器上,申请 8G 内存,会怎么样?
64 位操作系统,进程可以使用 128 TB 大小的虚拟内存空间,所以进程申请 8GB 内存是没问题的,因为进程申请内存是申请虚拟内存,只要不读写这个虚拟内存,操作系统就不会分配物理内存。
我们可以简单做个测试,我的服务器是 64 位操作系统,但是物理内存只有 2 GB。
现在,我在机器上,申请 4 GB 内存,注意下面代码只是单纯分配了虚拟内存,并没有使用该虚拟内存:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <string.h>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 大小的虚拟内存:
我们可以通过下面这条命令查看进程的虚拟内存大小:
# ps aux | grep alloc_4gUSER PID %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 <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <string.h>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;}
运行结果:
可以看到,在申请了 2GB 虚拟内存后,然后马上使用了这块虚拟内存,由于这台机器的物理内存只有 2 GB,所以发生了 OOM。
至此, 验证完成了。简单总结下:
在 32 位操作系统,因为进程最大只能申请 3 GB 大小的虚拟内存,所以直接申请 8G 内存,会申请失败。
在 64位 位操作系统,因为进程最大只能申请 128 TB 大小的虚拟内存,即使物理内存只有 4GB,申请 8G 内存也是没问题,因为申请的内存是虚拟内存,等这块虚拟内存被访问了,因为物理空间不够,就会发生 OOM。
好了,水完了!
边栏推荐
猜你喜欢
字符串 | 反转字符串 | 双指针法 | leecode刷题笔记
mysql + redis + flask + flask-sqlalchemy + flask-session 配置及项目打包移植部署
ICML 2022 | Out-of-Distribution检测与深最近的邻居
C# async 和 await 理解
Senior told me that the giant MySQL is through SSH connection
Notepad++安装插件
人体解析(Human Parse)开源数据集整理
Ways to prevent data fraud
富媒体在客服IM消息通信中的秒发实践
wpf实现简易画板功能(带截取画板,签名截图等等)
随机推荐
Django cannot link mysql database
Oracle Database Architecture
MySQL执行sql语句的机制
x86 exception handling and interrupt mechanism (2) interrupt vector table
redis主从复制
OC-块对象
【面试高频题】可逐步优化的链表高频题
es6的async函数
redis的缓存穿透、缓存雪崩、缓存击穿怎么搞?
MySQL查询性能优化七种武器之索引潜水
排序--快排(图解)
学生成绩查找系统
Redis的下载安装
[现代控制理论]6_稳定性_李雅普诺夫_Lyapunov
Ways to prevent data fraud
buck型三相PFC
log4net使用指南(winform版,sqlserver记录)
proto3-2语法
Summary of learning stages (knapsack problem)
enum in c language