作者wtchen (沒有存在感的人)
看板C_and_CPP
標題十三誡增修--10:不要在 stack 設置過大的變數以避免堆疊溢位
時間Mon May 23 22:02:35 2016
10.
不要在 stack 設置過大的變數以避免堆疊溢位(stack overflow)
由於編譯器會自行決定 stack 的上限,某些預設是數 KB 或數十KB,當變數所需的空
間過大時,很容易造成 stack overflow,程式亦隨之當掉(segmentation fault)。
可能造成堆疊溢位的原因包括遞迴太多次(多為程式設計缺陷),
或是在 stack 設置過大的變數。
錯誤例子:
int array[10000000]; // 在stack宣告過大陣列
std::array<int, 10000000> myarray; //在stack宣告過大std::array
正確例子:
C:
int *array = (int*) malloc( 10000000*sizeof(int) );
C++:
std::vector<int> v;
v.resize(10000000);
說明:建議將使用空間較大的變數用malloc/new配置在 heap 上,由於此時 stack
上只需配置一個 int* 的空間指到在heap的該變數,可避免 stack overflow。
使用 heap 時,雖然整個 process 可用的空間是有限的,但採用動態抓取
的方式,new 無法配置時會丟出 std::bad_alloc 例外,malloc 無法配置
時會回傳 null(註2),不會影響到正常使用下的程式功能
備註:
註1. 使用 heap 時,整個 process 可用的空間一樣是有限的,若是需要頻繁地
malloc / free 或 new / delete 較大的空間,需注意避免造成記憶體破碎
(memory fragmentation)。
註2. 由於Linux使用overcommit機制管理記憶體,malloc即使在記憶體不足時
仍然會回傳非NULL的address,同樣情形在Windows/Mac OS則會回傳NULL
(感謝 LiloHuang 補充)
補充資料:
-
https://zh.wikipedia.org/wiki/%E5%A0%86%E7%96%8A%E6%BA%A2%E4%BD%8D
-
http://stackoverflow.com/questions/3770457/what-is-memory-fragmentation
-
http://library.softwareverify.com/memory-fragmentation-your-worst-nightmare/
overcommit跟malloc:
-
http://goo.gl/V9krbB
-
http://goo.gl/5tCLQc
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 223.136.189.53
※ 文章網址: https://webptt.com/m.aspx?n=bbs/C_and_CPP/M.1464012158.A.3DA.html
1F:推 LiloHuang: Linux 上 malloc 回傳 non-NULL 不代表就是配置成功 05/23 22:20
2F:→ LiloHuang: 要留意預設的 optimistic memory allocation strategy 05/23 22:21
3F:推 kwpn: 若回傳non-NULL但是是失敗,要怎知道是失敗,有error code嗎 05/23 22:22
5F:→ LiloHuang: 沒有辦法知道,除非關掉 overcommit 的機制 (預設為開) 05/23 22:23
6F:→ wtchen: L大指的好像是realloc的情況?如果沒有辦法增加到想要 05/23 22:25
8F:→ LiloHuang: 不單是 realloc,呼叫 malloc 幾乎都會回傳 non-NULL 05/23 22:25
9F:→ LiloHuang: 因為採用 overcommit 的機制,建議把延伸閱讀看一下 05/23 22:26
10F:→ LiloHuang: 看過不少人以為 non-NULL 就是可以讀寫,其實不一定... 05/23 22:28
11F:→ wtchen: 這樣如果出現memory frag.不是很難debug嗎? 05/23 22:29
12F:推 LiloHuang: overcommit 某種程度可以讓系統有效的利用實體記憶體 05/23 22:32
13F:→ wtchen: kernel沒有限制user space使用memory的上限? 05/23 22:38
14F:→ LiloHuang: 這是兩件事情,單一 process 是否達到上限,跟一堆 05/23 22:40
15F:→ wtchen: 剛剛查了一下,Windows好像也有overcommit? 05/23 22:40
16F:→ wtchen: 所以windows會有同樣的問題嗎? 05/23 22:41
17F:→ LiloHuang: processes 配置了 1GB ,但是卻沒辦法正常存取是兩件事 05/23 22:41
18F:→ LiloHuang: Windows / Mac OS X 在記憶體不夠用時,皆會回傳 NULL 05/23 22:42
※ 編輯: wtchen (223.136.189.53), 05/23/2016 23:02:14
19F:→ tinlans: 標題殺人 被標題騙到很生氣地進來看 XD 05/25 12:02
抱歉造成誤解....
※ 編輯: wtchen (220.128.143.228), 05/25/2016 12:53:42
20F:推 hylkevin: linux會在需要實體記憶體時觸發swap或oom,所以頂多有 05/27 17:32
21F:→ hylkevin: 效能問題,應該不至於會error。 05/27 17:32
22F:推 LiloHuang: 不夠用就是會被 OOM killer 給砍掉... 不是無上限的 05/27 18:52
23F:→ LiloHuang: #include <cstdlib> 05/27 18:58
24F:→ LiloHuang: #include <cstring> 05/27 18:58
25F:→ LiloHuang: int main() { 05/27 18:59
26F:→ LiloHuang: const size_t CHUNK_SIZE = 1024*1024*1024; 05/27 18:59
27F:→ LiloHuang: const size_t NUMBER_OF_CHUNK = 1024; 05/27 18:59
28F:→ LiloHuang: void *chunks[NUMBER_OF_CHUNK]; 05/27 18:59
29F:→ LiloHuang: // allocate without mapping 05/27 18:59
30F:→ LiloHuang: for (size_t i=0; i<NUMBER_OF_CHUNK; i++) { 05/27 18:59
31F:→ LiloHuang: chunks[i] = malloc(CHUNK_SIZE); 05/27 19:00
32F:→ LiloHuang: } 05/27 19:00
33F:→ LiloHuang: // use memset to map physical memory 05/27 19:00
34F:→ LiloHuang: for (size_t i=0; i<NUMBER_OF_CHUNK; i++) { 05/27 19:00
35F:→ LiloHuang: memset(chunks[i], 0x0, CHUNK_SIZE); 05/27 19:00
36F:→ LiloHuang: } 05/27 19:00
37F:→ LiloHuang: // reclaim allocated chunks 05/27 19:00
38F:→ LiloHuang: for (size_t i=0; i<NUMBER_OF_CHUNK; i++) { 05/27 19:01
39F:→ LiloHuang: free(chunks[i]); 05/27 19:01
40F:→ LiloHuang: } 05/27 19:01
41F:→ LiloHuang: return 0; 05/27 19:01
42F:→ LiloHuang: } 05/27 19:01
43F:→ LiloHuang: 有興趣的自己跑跑看上面,看會不會被 OOM killer 砍掉 05/27 19:02
44F:→ LiloHuang: 順便檢查一下 malloc 的回傳值,是否都是 non-NULL 05/27 19:04
45F:→ LiloHuang: 加個 if (!chunks[i]) throw std::bad_alloc(); 之類的 05/27 19:07
46F:→ LiloHuang: 看到底是 OOM killer 砍掉還是 NULL dereference 掛掉 05/27 19:08
47F:→ LiloHuang: 這三個迴圈一定要分開跑,寫成一個迴圈就不一定會被砍 05/27 19:09
48F:→ wtchen: OOM不會選擇砍別的process嗎?如果在preempt RT kernel 05/27 19:16
49F:→ wtchen: 的話加上mlockall就會砍別的process吧? 05/27 19:16
51F:→ LiloHuang: 有可能別的 process 會被砍,假設它 oom_score 更高 05/27 19:20
52F:→ LiloHuang: 只是上面的範例,通常會是被砍的那一個... :) 05/27 19:20
53F:→ wtchen: 要寫的文章又多一篇 XD 05/27 20:32