作者:
wtchen (沒有存在感的人)
2016-05-23 22:02:3510. 不要在 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
作者: LiloHuang (十年一刻) 2016-05-23 22:20:00
Linux 上 malloc 回傳 non-NULL 不代表就是配置成功要留意預設的 optimistic memory allocation strategy
作者:
kwpn (ITSST)
2016-05-23 22:22:00若回傳non-NULL但是是失敗,要怎知道是失敗,有error code嗎
作者: LiloHuang (十年一刻) 2016-05-23 22:22:00
有興趣的可以看一下 Linux 文件
http://goo.gl/V9krbB沒有辦法知道,除非關掉 overcommit 的機制 (預設為開)
作者:
wtchen (沒有存在感的人)
2016-05-23 22:25:00L大指的好像是realloc的情況?如果沒有辦法增加到想要
作者: LiloHuang (十年一刻) 2016-05-23 22:25:00
再來一篇延伸閱讀給有興趣的
http://goo.gl/5tCLQc不單是 realloc,呼叫 malloc 幾乎都會回傳 non-NULL因為採用 overcommit 的機制,建議把延伸閱讀看一下看過不少人以為 non-NULL 就是可以讀寫,其實不一定...
作者:
wtchen (沒有存在感的人)
2016-05-23 22:29:00這樣如果出現memory frag.不是很難debug嗎?
作者: LiloHuang (十年一刻) 2016-05-23 22:32:00
overcommit 某種程度可以讓系統有效的利用實體記憶體
作者:
wtchen (沒有存在感的人)
2016-05-23 22:38:00kernel沒有限制user space使用memory的上限?
作者: LiloHuang (十年一刻) 2016-05-23 22:40:00
這是兩件事情,單一 process 是否達到上限,跟一堆
作者:
wtchen (沒有存在感的人)
2016-05-23 22:40:00剛剛查了一下,Windows好像也有overcommit?所以windows會有同樣的問題嗎?
作者: LiloHuang (十年一刻) 2016-05-23 22:41:00
processes 配置了 1GB ,但是卻沒辦法正常存取是兩件事Windows / Mac OS X 在記憶體不夠用時,皆會回傳 NULL
作者:
tinlans ( )
2016-05-25 12:02:00標題殺人 被標題騙到很生氣地進來看 XD
linux會在需要實體記憶體時觸發swap或oom,所以頂多有效能問題,應該不至於會error。
作者: LiloHuang (十年一刻) 2016-05-27 18:52:00
不夠用就是會被 OOM killer 給砍掉... 不是無上限的#include <cstdlib>#include <cstring>int main() {const size_t CHUNK_SIZE = 1024*1024*1024;const size_t NUMBER_OF_CHUNK = 1024;void *chunks[NUMBER_OF_CHUNK];// allocate without mappingfor (size_t i=0; i<NUMBER_OF_CHUNK; i++) {chunks[i] = malloc(CHUNK_SIZE);}// use memset to map physical memoryfor (size_t i=0; i<NUMBER_OF_CHUNK; i++) {memset(chunks[i], 0x0, CHUNK_SIZE);}// reclaim allocated chunksfor (size_t i=0; i<NUMBER_OF_CHUNK; i++) {free(chunks[i]);}return 0;}有興趣的自己跑跑看上面,看會不會被 OOM killer 砍掉順便檢查一下 malloc 的回傳值,是否都是 non-NULL加個 if (!chunks[i]) throw std::bad_alloc(); 之類的看到底是 OOM killer 砍掉還是 NULL dereference 掛掉這三個迴圈一定要分開跑,寫成一個迴圈就不一定會被砍
作者:
wtchen (沒有存在感的人)
2016-05-27 19:16:00OOM不會選擇砍別的process嗎?如果在preempt RT kernel的話加上mlockall就會砍別的process吧?
作者: LiloHuang (十年一刻) 2016-05-27 19:18:00
有評分機制,可參考 oom_kill.c
https://goo.gl/vVqyeo有可能別的 process 會被砍,假設它 oom_score 更高只是上面的範例,通常會是被砍的那一個... :)
作者:
wtchen (沒有存在感的人)
2016-05-27 20:32:00要寫的文章又多一篇 XD