C程序在Linux上耗尽内存

我想编写一个程序来消耗所有可用的内存来理解结果。 我听说linux一旦无法分配内存就开始查杀进程。

任何人都可以帮助我这样一个程序。

我已经写了下面的内容,但是内存似乎没有用尽:

#include <stdlib.h> int main() { while(1) { malloc(1024*1024); } return 0; } 

你应该写入分配的块。 如果你只是要求内存,linux可能只是提交一个预留的内存,但是什么都不会分配,直到内存被访问。

 int main() { while(1) { void *m = malloc(1024*1024); memset(m,0,1024*1024); } return 0; } 

但是,实际上只需要在每个页面上写入1个字节(x86上的4096个字节)。

Linux“提交”内存。 这意味着物理内存仅在进程首次尝试访问进程时才会给予进程, 而不是malloc第一次执行时进行。 要禁用此行为,请执行以下操作(以root用户身份):

 echo 2 > /proc/sys/vm/overcommit_memory 

然后尝试运行你的程序。

Linux使用,默认情况下,我喜欢称之为“伺机分配”。 这是基于观察到一些真实的程序分配比实际使用的更多的内存。 Linux使用它来适应更多的东西到内存中:它只在分配一个内存页时使用,而不是当它分配malloc(或mmap或sbrk)。

如果你在循环中做了这样的事情,你可能会获得更多的成功:

 memset(malloc(1024*1024L), 'w', 1024*1024L); 

一个鲜为人知的事实(尽管它已被充分证明) – 你可以(作为根源)防止OOM杀手将自己的过程(或任何其他过程)声称为其受害者之一。 这是从我的编辑器直接出来的片段,我(基于配置数据)锁定所有分配的内存,以避免被分页和(可选)告诉OOM杀手不要打扰我:

 static int set_priority(nex_payload_t *p) { struct sched_param sched; int maxpri, minpri; FILE *fp; int no_oom = -17; if (p->cfg.lock_memory) mlockall(MCL_CURRENT | MCL_FUTURE); if (p->cfg.prevent_oom) { fp = fopen("/proc/self/oom_adj", "w"); if (fp) { /* Don't OOM me, Bro! */ fprintf(fp, "%d", no_oom); fclose(fp); } } 

我没有显示我在做什么与调度程序参数,因为它与问题无关。

这将防止OOM杀手在有机会产生(在这种情况下)期望的效果之前得到你的过程。 实际上,您也将强制大多数其他进程磁盘。

所以,总之,看烟花的确很快

  1. 告诉OOM杀手不要打扰你
  2. 锁定你的记忆
  3. 在永无止境的循环中分配和初始化(零出)块,或者直到malloc()失败

一定要看看ulimit ,并以root身份运行你的测试。

我所展示的代码是一个守护进程的一部分,它不会失败,它运行的权重非常高(有选择地使用RR或FIFO调度程序),不能(永远不会)被分页出去。

看看这个程序。 当内存不足时,malloc开始返回0

 #include <stdlib.h> #include <stdio.h> int main() { while(1) { printf("malloc %d\n", (int)malloc(1024*1024)); } return 0; } 

在我的机器中,有一个合适的gb值,下面的代码使用了100%的内存,甚至把内存加入到swap中。 你可以看到你需要在每个页面只写一个字节: memset(m, 0, 1); ,如果你改变页面大小: #define PAGE_SZ (1<<12)到更大的页面大小: #define PAGE_SZ (1<<13)那么你将不会写入你分配的所有页面,因此你可以看到最重要的是该程序的内存消耗下降。

 #include <stdio.h> #include <stdlib.h> #include <string.h> #define PAGE_SZ (1<<12) int main() { int i; int gb = 2; // memory to consume in GB for (i = 0; i < ((unsigned long)gb<<30)/PAGE_SZ ; ++i) { void *m = malloc(PAGE_SZ); if (!m) break; memset(m, 0, 1); } printf("allocated %lu MB\n", ((unsigned long)i*PAGE_SZ)>>20); getchar(); return 0; } 

在32位Linux系统上,单个进程可以在其地址空间中分配的最大值大约为3Gb。

这意味着你不可能用一个单独的过程耗尽内存。

另一方面,在64位机器上,您可以尽可能多地分配。

正如其他人所指出的,还需要初始化内存,否则它实际上不会消耗页面。

如果操作系统没有剩余虚拟内存,或者进程超出了地址空间(或者不足以满足请求的分配),malloc将开始给出错误。

正如其他人所指出的那样,Linux的虚拟机过度使用也会影响到什么时候发生。

我无聊一次,做了这个。 得到这个吃了所有的内存,并需要强制重新启动,以使其重新工作。

 #include <stdlib.h> #include <unistd.h> int main(int argc, char** argv) { while(1) { malloc(1024 * 4); fork(); } } 

如果你所需要的只是强调系统,那么就有压力工具,这正是你想要的。 它可以作为大多数发行版的软件包。

我只是排除了@John La Rooy的片段:

 #include <stdlib.h> #include <stdio.h> int main() { while(1) { printf("malloc %d\n", (int)malloc(1024*1024)); } return 0; } 

但是它非常快速地耗尽了我的记忆,并导致系统挂起,所以我不得不重新启动它。

所以我建议你改一些代码。

例如:在我的ubuntu 16.04 LTS下面的代码需要大约1.5 GB的RAM,物理内存消耗从1.8 GB提高到3.3 GB,执行完后再下降到1.8GiB。尽管看起来我已经配置了300GiB RAM代码。

 #include <stdlib.h> #include <stdio.h> int main() { while(int i<300000) { printf("malloc %p\n", malloc(1024*1024)); i += 1; } return 0; } 

当索引i小于100000(即分配小于100 GB)时,物理或虚拟内存只是使用得很少(小于100MB),我不知道为什么,可能与虚拟内存有关。

有一件有趣的事情是,当物理内存开始缩小时,malloc()的地址肯定会返回变化,请参阅下面的图片链接。

我使用malloc()和calloc(),似乎在占用物理内存方面表现相似。

当物理内存开始收缩时,内存地址编号从48位变为28位