在 4GB 物理內(nèi)存的機(jī)器上,申請 8G 內(nèi)存會怎么樣?
大家好,我是小林。
早上看到讀者在群里討論這些面試題:
其中,第一個(gè)問題「在 4GB 物理內(nèi)存的機(jī)器上,申請 8G 內(nèi)存會怎么樣?」存在比較大的爭議,有人說會申請失敗,有的人說可以申請成功。
這個(gè)問題在沒有前置條件下,就說出答案就是耍流氓。因?yàn)樵?32 位操作系統(tǒng)和 64 位操作系統(tǒng)場景下,答案是不同的。
另外,我們還要看申請完 8G 內(nèi)存后會不會被使用,會被使用是一種情況,不會被使用又是另外一種情況了。
所以,我們要分場景討論。
應(yīng)用程序通過 malloc 函數(shù)申請內(nèi)存的時(shí)候,實(shí)際上申請的是虛擬內(nèi)存,此時(shí)并不會分配物理內(nèi)存。
當(dāng)應(yīng)用程序讀寫了這塊虛擬內(nèi)存,CPU 就會去訪問這個(gè)虛擬內(nèi)存, 這時(shí)會發(fā)現(xiàn)這個(gè)虛擬內(nèi)存沒有映射到物理內(nèi)存, CPU 就會產(chǎn)生缺頁中斷,進(jìn)程會從用戶態(tài)切換到內(nèi)核態(tài),并將缺頁中斷交給內(nèi)核的 Page Fault Handler (缺頁中斷函數(shù))處理。
缺頁中斷處理函數(shù)會看是否有空閑的物理內(nèi)存:
- 如果有,就直接分配物理內(nèi)存,并建立虛擬內(nèi)存與物理內(nèi)存之間的映射關(guān)系。
- 如果沒有空閑的物理內(nèi)存,那么內(nèi)核就會開始進(jìn)行回收內(nèi)存的工作,如果回收內(nèi)存工作結(jié)束后,空閑的物理內(nèi)存仍然無法滿足此次物理內(nèi)存的申請,那么內(nèi)核就會放最后的大招了觸發(fā) OOM (Out of Memory)機(jī)制。
32 位操作系統(tǒng)和 64 位操作系統(tǒng)的虛擬地址空間大小是不同的,在 Linux 操作系統(tǒng)中,虛擬地址空間的內(nèi)部又被分為內(nèi)核空間和用戶空間兩部分,如下所示:
通過這里可以看出:
- 32? 位系統(tǒng)的內(nèi)核空間占用1G?,位于最高處,剩下的3G 是用戶空間;
- 64? 位系統(tǒng)的內(nèi)核空間和用戶空間都是128T,分別占據(jù)整個(gè)內(nèi)存空間的最高和最低處,剩下的中間部分是未定義的。
現(xiàn)在可以回答這個(gè)問題了:在 32 位操作系統(tǒng)、4GB 物理內(nèi)存的機(jī)器上,申請 8GB 內(nèi)存,會怎么樣?
因?yàn)?32 位操作系統(tǒng),進(jìn)程最多只能申請 3 GB 大小的虛擬內(nèi)存空間,所以進(jìn)程申請 8GB 內(nèi)存,在申請?zhí)摂M內(nèi)存階段就會失?。ㄎ沂稚蠜]有 32 位操作系統(tǒng)測試,我估計(jì)失敗的原因是 OOM)。
在 64 位操作系統(tǒng)、4GB 物理內(nèi)存的機(jī)器上,申請 8G 內(nèi)存,會怎么樣?
64 位操作系統(tǒng),進(jìn)程可以使用 128 TB 大小的虛擬內(nèi)存空間,所以進(jìn)程申請 8GB 內(nèi)存是沒問題的,因?yàn)檫M(jìn)程申請內(nèi)存是申請?zhí)摂M內(nèi)存,只要不讀寫這個(gè)虛擬內(nèi)存,操作系統(tǒng)就不會分配物理內(nèi)存。
我們可以簡單做個(gè)測試,我的服務(wù)器是 64 位操作系統(tǒng),但是物理內(nèi)存只有 2 GB。
現(xiàn)在,我在機(jī)器上,申請 4 GB 內(nèi)存,注意下面代碼只是單純分配了虛擬內(nèi)存,并沒有使用該虛擬內(nèi)存:
#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èi)存分配\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("主線程調(diào)用malloc后,申請1gb大小得內(nèi)存,此內(nèi)存起始地址:0X%x\n" addr i );
getchar();
return 0;
然后運(yùn)行這個(gè)代碼,可以看到,我的物理內(nèi)存雖然只有 2GB,但是程序正常分配了 4GB 大小的虛擬內(nèi)存:
我們可以通過下面這條命令查看進(jìn)程的虛擬內(nèi)存大?。?/p>
# ps aux | grep alloc_4g
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 ./alloc_4g
其中,VSZ 就代表進(jìn)程使用的虛擬內(nèi)存大小,RSS 代表進(jìn)程使用的物理內(nèi)存大小??梢钥吹剑琕SZ 大小為 4198540,也就是 4GB 的虛擬內(nèi)存。
然后,我們改一下代碼,在申請完虛擬內(nèi)存后,通過 memset 函數(shù)使用這個(gè)虛擬內(nèi)存,看看會發(fā)生什么。
#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èi)存分配\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("主線程調(diào)用malloc后,申請1gb大小得內(nèi)存,此內(nèi)存起始地址:0X%x\n" addr i );
//訪問虛擬內(nèi)存
memset(addr i 0 s);
getchar();
return 0;
運(yùn)行結(jié)果:
可以看到,在申請了 2GB 虛擬內(nèi)存后,然后馬上使用了這塊虛擬內(nèi)存,由于這臺機(jī)器的物理內(nèi)存只有 2 GB,所以發(fā)生了 OOM。
至此, 驗(yàn)證完成了。簡單總結(jié)下:
- 在 32 位操作系統(tǒng),因?yàn)檫M(jìn)程最大只能申請 3 GB 大小的虛擬內(nèi)存,所以直接申請 8G 內(nèi)存,會申請失敗。
- 在 64位 位操作系統(tǒng),因?yàn)檫M(jìn)程最大只能申請 128 TB 大小的虛擬內(nèi)存,即使物理內(nèi)存只有 4GB,申請 8G 內(nèi)存也是沒問題,因?yàn)樯暾埖膬?nèi)存是虛擬內(nèi)存,等這塊虛擬內(nèi)存被訪問了,因?yàn)槲锢砜臻g不夠,就會發(fā)生 OOM。