摘要:本文測(cè)試環(huán)境如下一首先需要考慮的幾個(gè)問(wèn)題我們使用申請(qǐng)到的是物理內(nèi)存嗎使用能申請(qǐng)到的只有的物理內(nèi)存嗎申請(qǐng)到的內(nèi)存大小全都可以被用來(lái)嗎以上三個(gè)問(wèn)題,正是本次所要討論的內(nèi)容。
今天閱讀相關(guān)書(shū)籍的時(shí)候看到 "進(jìn)程中堆的最大申請(qǐng)數(shù)量" 這一問(wèn)題,我們知道使用malloc分配內(nèi)存是在堆Heap里面分配的,如果一臺(tái)機(jī)器一共有8GB物理內(nèi)存,空閑5GB,那么我們使用malloc( )就一定能夠申請(qǐng)到這5GB內(nèi)存嗎?理論上來(lái)說(shuō)確實(shí)如此,因?yàn)檫@些內(nèi)存未被其它進(jìn)程使用。但實(shí)際測(cè)試出來(lái)結(jié)果卻可能令人疑惑。
本文測(cè)試環(huán)境如下:
1 qi@qi:~$ uname -a2 Linux qi 5.4.0-89-generic #100~18.04.1-Ubuntu SMP Wed Sep 29 10:59:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux?
?
以上三個(gè)問(wèn)題,正是本次所要討論的內(nèi)容?,F(xiàn)在假定認(rèn)為以上三個(gè)陳述均正確,那么我們可以用以下程序測(cè)試malloc( )可以申請(qǐng)的內(nèi)存大小:
1 #include2 #include 3 #include <string.h> 4 5 unsigned long long int maximum = 0; 6 7 int main (int argc, char* argv[]) 8 { 9 unsigned int block_size[] = {1024*1024, 1024, 1};10 int i, count;11 12 for( int i=0; i<3; i++) {13 for(count=1;; count++) {14 void *block = malloc(maximum + block_size[i]*count);15 if( block ) {16 //memset(block, 0, maximum + block_size[i]*count);17 free(block);18 maximum = maximum + block_size[i]*count;19 } else {20 break;21 }22 }23 }24 25 printf("maximum malloc size is %llu bytes /n", maximum);26 27 return 0;28 }
?運(yùn)行以上程序,得到輸出為:
1 root@qi:/home/qi/test_park/elf_load# ./main 2 maximum malloc size is 24587279333 bytes
?可以看到,以上測(cè)試程序最大申請(qǐng)到了 22.9GB 的內(nèi)存,但是我的機(jī)器上實(shí)際內(nèi)存有多少呢?如下:
1 qi@qi:~/test_park/elf_load$ free2 total used free shared buff/cache available3 Mem: 8011016 2373760 3517884 719508 2119372 46546404 Swap: 15999996 0 15999996
很明顯,機(jī)器上最大的物理內(nèi)存也沒(méi)到8GB,如果你了解swap 交換空間,可能會(huì)說(shuō)Mem項(xiàng)和Swap項(xiàng)的total加起來(lái)似乎正好是22.9GB,但是另外一個(gè)問(wèn)題有來(lái)了,那就是這些內(nèi)存或者交換空間并不是全部空閑,包括系統(tǒng)內(nèi)核和系統(tǒng)界面等等也要占用一部分物理內(nèi)存,所以我們看到Mem項(xiàng)的 "available"的可用內(nèi)存只有大約4.5GB,所以結(jié)果就是,malloc( )申請(qǐng)到的內(nèi)存數(shù)量是遠(yuǎn)遠(yuǎn)大于我們實(shí)際的物理內(nèi)存的。既然malloc( )函數(shù)的實(shí)際輸出和我們的預(yù)期不相符,那是不是我們哪里用錯(cuò)了呢?不妨使用"man malloc"查看對(duì)其的官方解釋:
1 NOTES2 By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the mem‐3 ory really is available. In case it turns out that the system is out of memory, one or more processes will be killed by the OOM killer. For more4 information, see the description of /proc/sys/vm/overcommit_memory and /proc/sys/vm/oom_adj in proc(5), and the Linux kernel source file Documenta‐5 tion/vm/overcommit-accounting.
?果不其然,Note中說(shuō)明了就算malloc( )返回非NULL指針也不能保證該指針指向的內(nèi)存區(qū)域全都可以被該進(jìn)程使用。那么為什么會(huì)這樣呢?后面有提示,首先涉及到的最重要的一個(gè)設(shè)置就是 "/proc/sys/vm/overcommit_memory" 這一個(gè)文件,使用 "man proc" 找到有關(guān)其的說(shuō)明:
1 /proc/sys/vm/overcommit_memory 2 This file contains the kernel virtual memory accounting mode. Values are: 3 4 0: heuristic overcommit (this is the default) 5 1: always overcommit, never check 6 2: always check, never overcommit 7 8 In mode 0, calls of mmap(2) with MAP_NORESERVE are not checked, and the default 9 check is very weak, leading to the risk of getting a process "OOM-killed".10 11 In mode 1, the kernel pretends there is always enough memory, until memory actually12 runs out. One use case for this mode is scientific computing applications that13 employ large sparse arrays. In Linux kernel versions before 2.6.0, any nonzero14 value implies mode 1.15 16 In mode 2 (available since Linux 2.6), the total virtual address space that can be17 allocated (CommitLimit in /proc/meminfo) is calculated as18 19 CommitLimit = (total_RAM - total_huge_TLB) *20 overcommit_ratio / 100 + total_swap
可以看到,如果該文件內(nèi)容為0,mmap(malloc的內(nèi)部調(diào)用)將不檢查,有導(dǎo)致使用不存在內(nèi)存的風(fēng)險(xiǎn),如果文件內(nèi)容為1,則malloc( )可以申請(qǐng)的內(nèi)存可以非常大,我的機(jī)器上經(jīng)過(guò)測(cè)試可以達(dá)到90T,如果該文件內(nèi)容為2,那么所有可以申請(qǐng)的內(nèi)存為 "CommitLimit",具體可以通過(guò)公式或者 "cat /proc/meminfo | grep Limit"查看大小。那么這就能說(shuō)通為什么上面的程序可以malloc( )出22GB多的內(nèi)存了,查看 "/proc/sys/vm/overcommit_memory" 果不其然,內(nèi)容為0:
1 root@qi:/home/qi/test_park/elf_load# cat /proc/sys/vm/overcommit_memory 2 0
以上回答了第2個(gè)問(wèn)題中的一部分,那就是某些設(shè)置下,malloc( )可以申請(qǐng)到超出機(jī)器物理內(nèi)存的大小,為什么說(shuō)是一部分呢,因?yàn)榭缮暾?qǐng)的內(nèi)存不僅和上述設(shè)定相關(guān),還和機(jī)器的swap space相關(guān),如果你不了解或者沒(méi)聽(tīng)過(guò)( 事實(shí)上在你給你機(jī)器裝Linux系統(tǒng)的時(shí)候應(yīng)該碰到過(guò),那就是磁盤(pán)分區(qū)的時(shí)候會(huì)有一個(gè)swap設(shè)定)swap空間,只需要知道它是一種掛載在物理硬盤(pán)上,用來(lái)存放一些不太頻繁使用的內(nèi)存,是一種低速的物理內(nèi)存的擴(kuò)展,當(dāng)物理內(nèi)存不夠用時(shí),原先一些物理內(nèi)存中不常訪問(wèn)的內(nèi)容會(huì)被轉(zhuǎn)移到這里以讓出空間給其它進(jìn)程。所以swap空間也可以被malloc( )申請(qǐng)到。
由此,第2個(gè)問(wèn)題得到了全部的解答。這個(gè)時(shí)候你可能會(huì)說(shuō),第1個(gè)問(wèn)題應(yīng)該也有答案了,因?yàn)閙alloc( )不僅申請(qǐng)了8GB的物理內(nèi)存,還申請(qǐng)了15GB的swap硬盤(pán)空間作為擴(kuò)展內(nèi)存,甚至還可以申請(qǐng)大約90TB的不存在的內(nèi)存,所以第一個(gè)問(wèn)題就解決了嗎?
其實(shí)對(duì),但也不全對(duì),因?yàn)閙alloc( )這個(gè)時(shí)候申請(qǐng)了內(nèi)存,但沒(méi)有完全申請(qǐng),這就涉及到一個(gè)叫做 "Lazy Allocation" 的東東,類(lèi)似于fork的寫(xiě)時(shí)復(fù)制機(jī)制,當(dāng)你使用malloc( )時(shí),系統(tǒng)并沒(méi)有真正從物理內(nèi)存中分配,而是等到進(jìn)程要操作時(shí)才提供allocation,這也就解釋了我們剛開(kāi)頭申請(qǐng)了22.9GB的內(nèi)存都還沒(méi)有報(bào)段錯(cuò)誤的原因。只有當(dāng)你access這個(gè)內(nèi)存區(qū)域的時(shí)候才會(huì)真正分配,所以我們可以大膽的在程序里面加上memset,把上面貼出的代碼的memset那一行取消掉注釋?zhuān)缓笤龠\(yùn)行。如果你不想等太久,可以像我這樣:
1 root@qi:/home/qi/test_park/elf_load# echo 0 > /proc/sys/vm/overcommit_memory 2 root@qi:/home/qi/test_park/elf_load# swapoff -a3 root@qi:/home/qi/test_park/elf_load# echo 2 > /proc/sys/vm/overcommit_memory
以上命令是把交換空間禁用,這樣就可以減少可使用的內(nèi)存了,關(guān)閉交換空間后,如果/proc/sys/vm/overcommit_memory內(nèi)容為0,那么你可以malloc( )的內(nèi)存大小應(yīng)該為8GB左右,但是不是每一個(gè)字節(jié)都可以memset,大可以測(cè)試一下,會(huì)發(fā)現(xiàn)memset了6~7GB的內(nèi)存空間后程序報(bào)錯(cuò)異常退出,這是因?yàn)檫@個(gè)時(shí)候可使用的內(nèi)存也就這么大,這種情況下隨意使用malloc( )申請(qǐng)到的內(nèi)存是不安全的。如果/proc/sys/vm/overcommit_memory內(nèi)容為2,那么這個(gè)時(shí)候可申請(qǐng)的內(nèi)存就得看 "CommitLimit" 了,在我的機(jī)器上測(cè)試是只能申請(qǐng)1.5GB左右,這種情況下無(wú)論如何也不會(huì)訪問(wèn)非法內(nèi)存區(qū)域了,但是一個(gè)缺點(diǎn)是不能使用全部的空閑內(nèi)存,只能修改相應(yīng)的設(shè)置。
那么該如何知道實(shí)際可用的內(nèi)存大小呢?一種解決方案是查看 "/proc/meminfo" 中的available memory,乘個(gè)安全系數(shù)再來(lái)申請(qǐng)。
以上,三個(gè)問(wèn)題全都被解決,離專(zhuān)業(yè)的linuxer又近了一步~
?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/124812.html
閱讀 1093·2021-11-24 09:39
閱讀 2796·2021-09-26 09:55
閱讀 18340·2021-08-23 09:47
閱讀 3663·2019-08-30 15:52
閱讀 919·2019-08-29 13:49
閱讀 1078·2019-08-23 18:00
閱讀 910·2019-08-23 16:42
閱讀 1736·2019-08-23 14:28