
文档教程知识库【免费下载链接】CS-Base图解计算机网络、操作系统、计算机组成、数据库共 1000 张图 50 万字破除晦涩难懂的计算机基础知识让天下没有难懂的八股文 在线阅读https://xiaolincoding.com项目地址https://gitcode.com/GitHub_Trending/cs/CS-Base点击查看免费下载本文基于 CS-Base《图解系统》内存管理章节围绕经典面试题「在 4GB 物理内存的机器上申请 8G 内存会怎么样」展开。这个问题的答案取决于操作系统位数32/64 位、申请后是否实际访问、是否启用 Swap 三个前置条件。读完本文你将掌握 malloc 虚拟内存语义、缺页中断流程、overcommit_memory参数、Swap 换入换出与 OOM Killer 的完整知识链并能用ps aux、/proc/sys/vm/overcommit_memory、/proc/pid/maps、top等工具亲手复现实验、验证结论。问题背景为什么「8G 内存」的答案众说纷纭在技术社区里「在 4GB 物理内存的机器上申请 8G 内存会怎么样」一直存在争议有人说会申请失败有人说可以申请成功。这两种说法都对也都不完整——在没有前置条件下直接给出答案就是在耍流氓。要严谨地回答这个问题必须先明确三个前置条件操作系统是 32 位的还是 64 位的这决定了进程虚拟地址空间的上限是「申请阶段是否失败」的根源申请完 8G 内存后会不会被使用只申请不访问与申请后立刻读写完全是两种结局操作系统有没有使用 Swap 机制这决定了物理内存不足时进程是「被杀」还是「活着」。下面分场景逐一讨论。前置知识malloc 申请的是虚拟内存而不是物理内存应用程序通过malloc()函数申请内存的时候实际上申请的是虚拟内存此时并不会分配物理内存。当应用程序读写了这块虚拟内存CPU 就会去访问这个虚拟内存这时会发现这个虚拟内存没有映射到物理内存CPU 就会产生缺页中断进程会从用户态切换到内核态并将缺页中断交给内核的 Page Fault Handler缺页中断处理函数处理。缺页中断处理函数会检查是否有空闲的物理内存如果有就直接分配物理内存并建立虚拟内存与物理内存之间的映射关系如果没有空闲的物理内存内核就会开始进行回收内存的工作如果回收工作结束后空闲物理内存仍然无法满足此次申请内核就会放出最后的大招——触发OOMOut of Memory机制。关于 malloc 更底层的实现brk/mmap 两条分配路径、128KB 阈值、malloc(1) 实际预分配 132KB、free 是否归还操作系统等可进一步阅读本仓库的 malloc 是如何分配内存的 一文。32 位与 64 位系统的虚拟地址空间大小32 位操作系统和 64 位操作系统的虚拟地址空间大小是不同的。在 Linux 操作系统中虚拟地址空间的内部又被分为内核空间和用户空间两部分32 位系统内核空间占用1G位于最高处剩下的3G是用户空间64 位系统内核空间和用户空间都是128T分别占据整个内存空间的最高和最低处剩下的中间部分是未定义的。为什么虚拟地址空间要这样划分、进程的虚拟地址如何通过 MMU 与页表映射到物理地址、为什么会有分段、分页、多级页表与 TLB 等机制可阅读本仓库的 为什么要有虚拟内存 一文作为理论基础。32 位系统场景申请 8GB 内存直接申请失败现在可以回答这个问题了在 32 位操作系统、4GB 物理内存的机器上申请 8GB 内存会怎么样因为 32 位操作系统下进程最多只能申请 3 GB 大小的虚拟内存空间用户空间上限所以进程申请 8GB 内存的话在申请虚拟内存的阶段就会失败——连虚拟内存都申请不到根本轮不到物理内存出场。失败的错误大概率是Cannot allocate memory无法申请内存失败。64 位系统场景申请 8G 虚拟内存没有任何问题在 64 位操作系统、4GB 物理内存的机器上申请 8G 内存会怎么样64 位操作系统下进程可以使用128 TB 大小的虚拟内存空间所以进程申请 8GB 内存是没问题的——因为进程申请内存是申请虚拟内存只要不读写这个虚拟内存操作系统就不会分配物理内存。我们可以做一个简单的实验验证。作者的服务器是 64 位操作系统但物理内存只有 2 GB。现在在这台机器上连续申请 4 次 1 GB 内存也就是一共申请 4 GB 内存。注意下面的代码只是单纯分配了虚拟内存并没有使用该虚拟内存#include stdio.h #include stdlib.h #include string.h #include errno.h #define MEM_SIZE 1024 * 1024 * 1024 int main() { char* addr[4]; int i 0; for(i 0; i 4; i) { addr[i] (char*) malloc(MEM_SIZE); if(!addr[i]) { printf(执行 malloc 失败错误%s\n,strerror(errno)); return -1; } printf(主线程调用 malloc 后申请 1gb 大小得内存此内存起始地址0X%p\n, addr[i]); } //输入任意字符后才结束 getchar(); return 0; }运行这段代码可以看到虽然物理内存只有 2GB但程序正常分配了 4GB 大小的虚拟内存——这正印证了「malloc 申请的是虚拟内存不访问就不占用物理内存」的结论。通过下面这条命令可以查看进程test的虚拟内存大小# ps aux | grep test USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 7797 0.0 0.0 4198540 352 pts/1 S 16:58 0:00 ./test其中VSZ 代表进程使用的虚拟内存大小RSS 代表进程使用的物理内存大小。可以看到VSZ 大小为 4198540也就是约 4GB 的虚拟内存而 RSS 只有 352KB——物理内存几乎没有被占用。为什么有的读者申请 4GB 虚拟内存会失败有读者反馈说自己做了同样的实验却发现在 64 位操作系统上申请 4GB 虚拟内存时失败了错误提示正是Cannot allocate memory。这是为什么呢排查后发现这与 Linux 中的overcommit_memory参数有关。可以使用下面的命令查看这个参数cat /proc/sys/vm/overcommit_memory该参数接受三个值值含义说明0默认值Heuristic overcommit handling允许 overcommit但过于明目张胆的 overcommit 会被拒绝。比如 malloc 一次性申请的内存大小就超过了系统总内存。Heuristic 的意思是「试探式的」内核利用某种算法猜测内存申请是否合理大概可以理解为单次申请不能超过 free memory free swap pagecache 的大小 SLAB 中可回收的部分超过就会拒绝 overcommit1Always overcommit允许 overcommit对内存申请来者不拒2Dont overcommit禁止 overcommit当时那位读者的overcommit_memory参数是默认值 0所以申请失败的原因可能是内核认为申请的内存太大了、不合理于是malloc()返回了Cannot allocate memory错误。申请 4GB 虚拟内存失败的同学可以将overcommit_memory设置为 1echo 1 /proc/sys/vm/overcommit_memory设置完为 1 后那台机器就可以正常申请 4GB 虚拟内存了。需要说明的是作者自己的环境overcommit_memory也是 0在 64 位系统、2G 物理内存场景下同样成功申请了 4G 内存。怀疑不同版本的内核在overcommit_memory为 0 时检测内存申请是否合理的算法可能不同。总之如果申请大内存时不想被内核「检测内存申请是否合理」的算法干扰将overcommit_memory设置为 1 即可。那么将overcommit_memory设置为 1 之后64 位的主机就可以申请接近 128T 虚拟内存了吗不一定还得看服务器的物理内存大小。读者的服务器物理内存是 2 GB实验后发现进程还没申请到 128T 虚拟内存就被杀死了。注意这次是killed而不是Cannot Allocate Memory——说明并不是内存申请有问题而是触发了 OOM。为什么会触发 OOM因为即使 malloc 申请的是虚拟内存、不访问就不映射物理内存申请虚拟内存的过程中还是会使用到物理内存比如内核保存虚拟内存的数据结构也占用物理内存。如果主机只有 2GB 物理内存大概率会触发 OOM。可以使用top命令连续按两下 m 键通过进度条观察物理内存使用情况可以看到申请虚拟内存的过程中物理内存使用量一直在增长。直到直接内存回收之后也无法回收出一块空间供这个进程使用这时就会触发 OOM给所有能杀死的进程打分分数越高的进程越容易被杀死。在这里当然是这个进程得分最高操作系统就将它杀死所以最后出现的是 killed而不是Cannot allocate memory。那么 2GB 物理内存的 64 位操作系统就不能申请 128T 的虚拟内存了吗其实可以上面的情况是还没开启 Swap 的情况。使用swapfile 方式开启 1GB 的 Swap 空间之后再实验发现虽然出现了Cannot allocate memory但到这一步其实已经成功了——计算一下已经申请了127.998T 虚拟内存。实际上我们是不可能申请完整个 128T 的用户空间的因为程序运行本身也需要申请虚拟空间。再试试申请 127T 虚拟内存进程既没有被杀死也没有出现Cannot allocate memory正好是 127T 虚拟内存空间。在top中可以看到这个申请了 127T 虚拟内存的进程。Swap 机制的作用物理内存不够时它是最后的缓冲前面讨论了 32 位/64 位操作系统环境下申请的虚拟内存超过物理内存后会怎么样在32 位操作系统因为进程最大只能申请 3 GB 大小的虚拟内存所以直接申请 8G 内存会申请失败在64 位操作系统因为进程最大只能申请 128 TB 大小的虚拟内存即使物理内存只有 4GB申请 8G 内存也没问题因为申请的是虚拟内存。程序申请的虚拟内存如果没有被使用它是不会占用物理空间的。当访问这块虚拟内存后操作系统才会进行物理内存分配。如果申请的物理内存大小超过了空闲物理内存大小就要看操作系统有没有开启 Swap 机制没有开启 Swap 机制程序会直接 OOM有开启 Swap 机制程序可以正常运行。什么是 Swap当系统的物理内存不够用的时候就需要将物理内存中的一部分空间释放出来以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序这些被释放的空间会被临时保存到磁盘等到那些程序要运行时再从磁盘中恢复保存的数据到内存中。另外当内存使用存在压力的时候会开始触发内存回收行为会把这些不常访问的内存先写到磁盘中然后释放这些内存给其他更需要的进程使用。再次访问这些内存时重新从磁盘读入内存就可以了。这种将内存数据换出磁盘、又从磁盘恢复数据到内存的过程就是Swap 机制负责的。Swap 就是把一块磁盘空间或者本地文件当成内存来使用它包含换出和换入两个过程换出Swap Out把进程暂时不用的内存数据存储到磁盘中并释放这些数据占用的内存换入Swap In在进程再次访问这些内存的时候把它们从磁盘读到内存中来。使用 Swap 机制的优点是应用程序实际可以使用的内存空间将远远超过系统的物理内存。由于硬盘空间的价格远比内存低这种方式无疑是经济实惠的。当然频繁地读写硬盘会显著降低操作系统的运行速率这也是 Swap 的弊端。Swap 在什么场景下触发Linux 中的 Swap 机制会在内存不足和内存闲置两种场景下触发内存不足当系统需要的内存超过了可用的物理内存时内核会将内存中不常使用的内存页交换到磁盘上为当前进程让出内存保证正在执行的进程的可用性。这个内存回收的过程是强制的直接内存回收Direct Page Reclaim。直接内存回收是同步的过程会阻塞当前申请内存的进程内存闲置应用程序在启动阶段使用的大量内存在启动后往往都不会再使用通过后台运行的守护进程kSwapd可以将这部分只使用一次的内存交换到磁盘上为其他内存申请预留空间。kSwapd 是 Linux 负责页面置换Page replacement的守护进程它会在空闲内存低于一定水位时回收内存页中的空闲内存保证系统中其他进程可以尽快获得申请的内存。kSwapd 是后台进程所以回收内存的过程是异步的不会阻塞当前申请内存的进程。关于内存回收更完整的机制——文件页与匿名页的回收差异、LRU 算法、swappiness、min_free_kbytes、NUMA 下的zone_reclaim_mode以及 OOM Killer 的oom_badness()打分算法可阅读本仓库的 内存满了会发生什么 一文。如何启用 SwapLinux 提供了两种不同的方法启用 SwapSwap 分区Swap Partition硬盘上的独立区域该区域只会用于交换分区其他文件不能存储在该区域上。可以使用swapon -s命令查看当前系统上的交换分区Swap 文件Swapfile文件系统中的特殊文件它与文件系统中的其他文件没有太多区别。Swap 换入换出的是什么类型的内存内核缓存的文件数据因为都有对应的磁盘文件所以在回收文件数据的时候直接写回到对应的文件就可以了。但是像进程的堆、栈数据等它们没有实际载体这部分内存被称为匿名页。而且这部分内存很可能还要再次被访问所以不能直接释放内存于是就需要有一个能保存匿名页的磁盘载体这个载体就是 Swap 分区。匿名页回收的方式就是通过 Linux 的 Swap 机制Swap 会把不常访问的内存先写到磁盘中然后释放这些内存给其他更需要的进程使用再次访问这些内存时重新从磁盘读入内存就可以了。接下来通过两个实验看看申请的物理内存超过物理内存会怎样实验一没有开启 Swap 机制实验二有开启 Swap 机制。实验一没有开启 Swap 机制进程被 OOM 杀死作者的服务器是 64 位操作系统物理内存只有 2 GB而且没有 Swap 分区。改造前面的代码在申请完 4GB 虚拟内存后通过memset函数实际访问这块虚拟内存看看在没有 Swap 分区的情况下会发生什么#include stdio.h #include stdlib.h #include string.h #include errno.h #define MEM_SIZE 1024 * 1024 * 1024 int main() { char* addr[4]; int i 0; for(i 0; i 4; i) { addr[i] (char*) malloc(MEM_SIZE); if(!addr[i]) { printf(执行 malloc 失败错误%s\n,strerror(errno)); return -1; } printf(主线程调用 malloc 后申请 1gb 大小得内存此内存起始地址0X%p\n, addr[i]); } for(i 0; i 4; i) { printf(开始访问第 %d 块虚拟内存 (每一块虚拟内存为 1 GB)\n, i 1); memset(addr[i], 0, MEM_SIZE); } //输入任意字符后才结束 getchar(); return 0; }运行结果在访问第 2 块虚拟内存的时候每一块 1 GB此时已访问总量超过机器的物理内存 2GB进程test被操作系统杀掉了。通过查看message系统日志可以发现该进程是被操作系统的OOM Killer 机制杀掉的日志里报错了Out of memory也就是发生了 OOM内存溢出错误。什么是 OOM内存溢出Out Of Memory简称 OOM是指应用系统中存在无法回收的内存或使用的内存过多最终使得程序运行要用到的内存大于能提供的最大内存。此时程序就运行不了系统会提示内存溢出。实验二有开启 Swap 机制32GB 内存正常使用作者用 MacBook Pro 笔记本做测试64 位操作系统物理内存 8 GBSwap 分区大小为 1 GB。注意macOS 的 Swap 分区总大小是动态变化的——当没有使用 Swap 分区时总大小是 0当使用了 Swap 分区总大小会增加至 1 GB当已使用大小超过 1 GB 时总大小增加到 2 GB超过 2 GB 时增加到 3GB如此往复。这是 macOS 自己的实现Linux 的 Swap 分区则是固定大小的不会根据使用情况自动增长。为了方便观察磁盘 I/O 情况改进一下前面的代码分配完 32 GB 虚拟内存后笔记本物理内存是 8 GB通过一个 while 循环频繁访问虚拟内存#include stdio.h #include stdlib.h #include string.h #define MEM_SIZE 32 * 1024 * 1024 * 1024 int main() { char* addr (char*) malloc((long)MEM_SIZE); printf(主线程调用 malloc 后目前共申请了 32gb 的虚拟内存\n); //循环频繁访问虚拟内存 while(1) { printf(开始访问 32gb 大小的虚拟内存...\n); memset(addr, 0, (long)MEM_SIZE); } return 0; }运行结果在有 Swap 分区的情况下即使笔记本物理内存只有 8 GB申请并使用 32 GB 内存也没问题程序正常运行没有发生 OOM。从top可以看到进程的内存显示 32 GB这里不要理解为占用的物理内存应理解为已被访问过的虚拟内存大小也就是在物理内存「呆过」的内存大小系统已使用的 Swap 分区达到2.3 GB。此时笔记本的磁盘开始出现「沙沙」的声音查看磁盘 I/O 情况可以看到磁盘 I/O 达到了一个很高的峰值——这就是 Swap 频繁换入换出带来的代价内存数据被换出到磁盘、再次访问时又从磁盘读入频繁的磁盘读写显著拖慢了系统。有了 Swap 分区进程可以使用无上限的内存吗当然不是。把上面的代码改成申请 64GB 内存后当进程用到56 GB已被访问的虚拟内存大小的时候进程就被系统 kill 掉了。当系统多次尝试回收内存还是无法满足所需使用的内存大小时进程就会被系统 kill 掉意味着发生了 OOM。总结不同场景下的最终答案至此验证完成。简单总结一下在 32 位操作系统因为进程理论上最大能申请 3 GB 大小的虚拟内存所以直接申请 8G 内存会申请失败在 64 位操作系统因为进程理论上最大能申请 128 TB 大小的虚拟内存即使物理内存只有 4GB申请 8G 内存也没问题因为申请的内存是虚拟内存。如果这块虚拟内存被访问了要看系统有没有 Swap 分区没有 Swap 分区因为物理空间不够进程会被操作系统杀掉原因是OOM内存溢出有 Swap 分区即使物理内存只有 4GB程序也能正常使用 8GB 的内存进程可以正常运行。面试作答速记围绕这道面试题可以按以下三层递进组织答案每一层都有对应的验证手段申请阶段malloc 申请的是虚拟内存能否申请成功取决于地址空间上限与overcommit_memory策略。32 位下 3GB 上限直接判死64 位下用cat /proc/sys/vm/overcommit_memory判断策略值 0 时单次超大申请可能被拒值 1 时来者不拒值 2 时禁止 overcommit访问阶段虚拟内存被读写时通过缺页中断按需映射物理内存可用ps aux中的 VSZ虚拟内存与 RSS物理内存对照观察也可用top按两次 m 看物理内存进度条增长物理内存耗尽阶段先内存回收详见内存回收一文回收无果则触发 OOM Killer 按oom_badness()打分杀进程开启 Swap 后进程可借助磁盘临时存储超过物理内存的匿名页代价是磁盘 I/O 飙升、系统变卡。延伸阅读为什么要有虚拟内存虚拟地址与物理地址映射、分段、分页、多级页表、TLB、Linux 虚拟地址空间布局malloc 是如何分配内存的brk/mmap 两条分配路径、malloc(1) 预分配 132KB、free 的释放语义内存满了会发生什么文件页/匿名页回收、kswapd 水位、swappiness、OOM Killer 打分与oom_score_adj保护进程的方法图解系统介绍操作系统进程管理、内存管理、文件系统、设备管理、网络系统五大结构的完整知识目录。赞分享文档教程知识库【免费下载链接】CS-Base图解计算机网络、操作系统、计算机组成、数据库共 1000 张图 50 万字破除晦涩难懂的计算机基础知识让天下没有难懂的八股文 在线阅读https://xiaolincoding.com项目地址https://gitcode.com/GitHub_Trending/cs/CS-Base点击查看免费下载相关推荐Compound Engineering 终极指南33 个 AI 技能如何让开发越来越快Compound Engineering 终极指南33 个 AI 技能如何让开发越来越快 Compound Engineering 是一款面向 Claude人工智能AI 技能AI 插件VidBee 实测3 步搞定 1000 网站视频下载VidBee 实测3 步搞定 1000 网站视频下载 想把一节两小时的在线教程存到平板上离线看网页上却只有分享按钮视频站要求登录抓到的流地址几分钟就失桌面应用音视频AI 应用语音CS-Base 图解系统为什么要有虚拟内存从分段分页到多级页表与 TLB 的完整内存管理图解CS Base 图解系统为什么要有虚拟内存从分段分页到多级页表与 TLB 的完整内存管理图解 本篇技术指南以 CS Base 仓库《图解系统》中 为什么要有文档教程知识库上一篇QMCDecode让QQ音乐加密文件重获自由的完整解决方案下一篇Boss Show Time4大招聘平台智能时间显示插件让你抓住最新工作机会创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考