摘要:本文測試環境如下一首先需要考慮的幾個問題我們使用申請到的是物理內存嗎使用能申請到的只有的物理內存嗎申請到的內存大小全都可以被用來嗎以上三個問題,正是本次所要討論的內容。
今天閱讀相關書籍的時候看到 "進程中堆的最大申請數量" 這一問題,我們知道使用malloc分配內存是在堆Heap里面分配的,如果一臺機器一共有8GB物理內存,空閑5GB,那么我們使用malloc( )就一定能夠申請到這5GB內存嗎?理論上來說確實如此,因為這些內存未被其它進程使用。但實際測試出來結果卻可能令人疑惑。
本文測試環境如下:
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?
?
以上三個問題,正是本次所要討論的內容。現在假定認為以上三個陳述均正確,那么我們可以用以下程序測試malloc( )可以申請的內存大小:
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 }
?運行以上程序,得到輸出為:
1 root@qi:/home/qi/test_park/elf_load# ./main 2 maximum malloc size is 24587279333 bytes
?可以看到,以上測試程序最大申請到了 22.9GB 的內存,但是我的機器上實際內存有多少呢?如下:
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
很明顯,機器上最大的物理內存也沒到8GB,如果你了解swap 交換空間,可能會說Mem項和Swap項的total加起來似乎正好是22.9GB,但是另外一個問題有來了,那就是這些內存或者交換空間并不是全部空閑,包括系統內核和系統界面等等也要占用一部分物理內存,所以我們看到Mem項的 "available"的可用內存只有大約4.5GB,所以結果就是,malloc( )申請到的內存數量是遠遠大于我們實際的物理內存的。既然malloc( )函數的實際輸出和我們的預期不相符,那是不是我們哪里用錯了呢?不妨使用"man malloc"查看對其的官方解釋:
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中說明了就算malloc( )返回非NULL指針也不能保證該指針指向的內存區域全都可以被該進程使用。那么為什么會這樣呢?后面有提示,首先涉及到的最重要的一個設置就是 "/proc/sys/vm/overcommit_memory" 這一個文件,使用 "man proc" 找到有關其的說明:
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
可以看到,如果該文件內容為0,mmap(malloc的內部調用)將不檢查,有導致使用不存在內存的風險,如果文件內容為1,則malloc( )可以申請的內存可以非常大,我的機器上經過測試可以達到90T,如果該文件內容為2,那么所有可以申請的內存為 "CommitLimit",具體可以通過公式或者 "cat /proc/meminfo | grep Limit"查看大小。那么這就能說通為什么上面的程序可以malloc( )出22GB多的內存了,查看 "/proc/sys/vm/overcommit_memory" 果不其然,內容為0:
1 root@qi:/home/qi/test_park/elf_load# cat /proc/sys/vm/overcommit_memory 2 0
以上回答了第2個問題中的一部分,那就是某些設置下,malloc( )可以申請到超出機器物理內存的大小,為什么說是一部分呢,因為可申請的內存不僅和上述設定相關,還和機器的swap space相關,如果你不了解或者沒聽過( 事實上在你給你機器裝Linux系統的時候應該碰到過,那就是磁盤分區的時候會有一個swap設定)swap空間,只需要知道它是一種掛載在物理硬盤上,用來存放一些不太頻繁使用的內存,是一種低速的物理內存的擴展,當物理內存不夠用時,原先一些物理內存中不常訪問的內容會被轉移到這里以讓出空間給其它進程。所以swap空間也可以被malloc( )申請到。
由此,第2個問題得到了全部的解答。這個時候你可能會說,第1個問題應該也有答案了,因為malloc( )不僅申請了8GB的物理內存,還申請了15GB的swap硬盤空間作為擴展內存,甚至還可以申請大約90TB的不存在的內存,所以第一個問題就解決了嗎?
其實對,但也不全對,因為malloc( )這個時候申請了內存,但沒有完全申請,這就涉及到一個叫做 "Lazy Allocation" 的東東,類似于fork的寫時復制機制,當你使用malloc( )時,系統并沒有真正從物理內存中分配,而是等到進程要操作時才提供allocation,這也就解釋了我們剛開頭申請了22.9GB的內存都還沒有報段錯誤的原因。只有當你access這個內存區域的時候才會真正分配,所以我們可以大膽的在程序里面加上memset,把上面貼出的代碼的memset那一行取消掉注釋,然后再運行。如果你不想等太久,可以像我這樣:
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
以上命令是把交換空間禁用,這樣就可以減少可使用的內存了,關閉交換空間后,如果/proc/sys/vm/overcommit_memory內容為0,那么你可以malloc( )的內存大小應該為8GB左右,但是不是每一個字節都可以memset,大可以測試一下,會發現memset了6~7GB的內存空間后程序報錯異常退出,這是因為這個時候可使用的內存也就這么大,這種情況下隨意使用malloc( )申請到的內存是不安全的。如果/proc/sys/vm/overcommit_memory內容為2,那么這個時候可申請的內存就得看 "CommitLimit" 了,在我的機器上測試是只能申請1.5GB左右,這種情況下無論如何也不會訪問非法內存區域了,但是一個缺點是不能使用全部的空閑內存,只能修改相應的設置。
那么該如何知道實際可用的內存大小呢?一種解決方案是查看 "/proc/meminfo" 中的available memory,乘個安全系數再來申請。
以上,三個問題全都被解決,離專業的linuxer又近了一步~
?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/124812.html
閱讀 953·2021-11-24 09:39
閱讀 2689·2021-09-26 09:55
閱讀 14154·2021-08-23 09:47
閱讀 3577·2019-08-30 15:52
閱讀 848·2019-08-29 13:49
閱讀 997·2019-08-23 18:00
閱讀 844·2019-08-23 16:42
閱讀 1635·2019-08-23 14:28