十三诫增修--10:不要在 stack 设置过大的变量以避免堆叠溢位

楼主: wtchen (没有存在感的人)   2016-05-23 22:02:35
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
作者: 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:00
L大指的好像是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:00
kernel没有限制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
作者: hylkevin (19s)   2016-05-27 17:32:00
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:00
OOM不会选择砍别的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

Links booklink

Contact Us: admin [ a t ] ucptt.com