作者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/cn.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