我正在写一些代码,并不断崩溃。 后来挖掘转储后,我意识到我超过了堆的最大限制(如果我已经在malloc上添加了一个检查,生活会更容易)。 虽然我修正了这个问题,有没有办法增加我的堆大小?
PS:这里有一个非常类似的问题 ,但答复对我来说还不清楚。
堆通常与您的架构上可寻址的虚拟内存一样大。
您应该使用ulimit -a
命令检查您的系统当前的限制,并寻找这条线的最大memory size (kbytes, -m) 3008828
,我的OpenSuse 11.4 x86_64的这一行与〜3.5 GiB的RAM说我有大约3GB的RAM每个进程。
然后,您可以使用这个简单的程序真正测试您的系统来检查每个进程的最大可用内存:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char* argv[]){ size_t oneHundredMiB=100*1048576; size_t maxMemMiB=0; void *memPointer = NULL; do{ if(memPointer != NULL){ printf("Max Tested Memory = %zi\n",maxMemMiB); memset(memPointer,0,maxMemMiB); free(memPointer); } maxMemMiB+=oneHundredMiB; memPointer=malloc(maxMemMiB); }while(memPointer != NULL); printf("Max Usable Memory aprox = %zi\n",maxMemMiB-oneHundredMiB); return 0; }
这个程序以100MiB的增量获取内存,呈现当前分配的内存,分配0,然后释放内存。 当系统不能提供更多的内存时,返回NULL,并显示RAM的最大最大可用数量。
警告的是,你的系统将在最后阶段开始大量交换内存。 根据您的系统配置,内核可能决定杀死一些进程。 我使用100 MiB的增量,所以有一些应用程序和系统有一些喘息的空间。 你应该关闭任何你不想崩溃的东西。
话虽如此。 在我写这个系统的时候,没有任何事情发生。 上面的程序和ulimit -a
几乎没有什么ulimit -a
。 不同的是,它实际上测试了内存,并通过memset()
确认内存已被使用。
在Ubuntu 10.04×86虚拟机上使用256M内存和400MiB交换的比较ulimit报告是memory size (kbytes, -m) unlimited
,我的小程序报告了524.288.000字节,这大致是组合内存和交换,折扣其他软件和内核使用的ram。
编辑:正如亚当Zalcman写道, ulimit -m
不再是新的2.6和更高的Linux内核荣幸,所以我站在纠正。 但ulimit -v
很荣幸。 对于实际的结果,您应该用-v替换-m,然后查找virtual memory (kbytes, -v) 4515440
。 看起来只是偶然的,我的suse box的-m值与我的小实用程序报告一致。 你应该记住,这是由内核分配的虚拟内存,如果物理内存不足,将需要交换空间来弥补。
如果你想知道有多少物理ram是可用的,而不会打扰任何进程或系统,你可以使用
long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;
这将排除缓存和缓冲区内存,所以这个数字可能远小于实际可用的内存。 操作系统缓存可以安静的大,他们的驱逐可以提供所需的额外的内存,但这是由内核处理。
堆和内存管理是由C库提供的工具(可能是glibc)。 它每次执行malloc()
都会维护堆并返回大块的内存。 它不知道堆大小的限制:每次你请求的内存超过了堆的可用内存时,它就会向内核请求更多的内存(使用sbrk()
或mmap()
)。
默认情况下,内核几乎总是会在被问到的时候给你更多的内存。 这意味着malloc()
将始终返回一个有效的地址。 只有当你第一次引用一个分配的页面时,内核才会真的为你找到一个页面。 如果发现不能交给你一个,它会运行一个OOM杀手,根据一定程度的不良行为 (包括你的进程和子进程的虚拟内存大小,级别,总体运行时间等)选择一个受害者并发送一个SIGTERM
。 这种内存管理技术被称为overcommit,当/proc/sys/vm/overcommit_memory
为0或1时,内核使用该技术。有关详细信息,请参阅内核文档中的overcommit-accounting 。
通过在/proc/sys/vm/overcommit_memory
写入2,可以禁用overcommit。 如果你这样做的话,内核会在承诺之前检查它是否有内存。 这将导致malloc()
返回NULL,如果没有更多的内存可用。
您还可以在进程可以使用setrlimit()
和RLIMIT_AS
或使用ulimit -v
命令分配的虚拟内存上设置限制。 不管上面描述的overcommit设置如果进程试图分配更多的内存比限制,内核将拒绝它, malloc()
将返回NULL。 注意在现代Linux内核(包括整个2.6.x系列)中,常驻大小(带RLIMIT_RSS
或ulimit -m
命令的setrlimit()
)的限制是无效的。
下面的会话是在内核2.6.32上运行,4GB RAM和8GB交换。
$ cat bigmem.c #include <stdlib.h> #include <stdio.h> int main() { int i = 0; for (; i < 13*1024; i++) { void* p = malloc(1024*1024); if (p == NULL) { fprintf(stderr, "malloc() returned NULL on %dth request\n", i); return 1; } } printf("Allocated it all\n"); return 0; } $ cc -o bigmem bigmem.c $ cat /proc/sys/vm/overcommit_memory 0 $ ./bigmem Allocated it all $ sudo bash -c "echo 2 > /proc/sys/vm/overcommit_memory" $ cat /proc/sys/vm/overcommit_memory 2 $ ./bigmem malloc() returned NULL on 8519th request $ sudo bash -c "echo 0 > /proc/sys/vm/overcommit_memory" $ cat /proc/sys/vm/overcommit_memory 0 $ ./bigmem Allocated it all $ ulimit -v $(( 1024*1024 )) $ ./bigmem malloc() returned NULL on 1026th request $
在上面的例子中,交换或者OOM kill是永远不会发生的,但是如果这个进程实际上尝试去触及所有分配的内存,这将会发生很大的变化。
要直接回答您的问题:除非使用ulimit -v
命令明确设置了虚拟内存限制,否则除了机器物理资源或地址空间的逻辑限制(在32位系统中相关)以外,没有堆大小限制。 您的glibc将继续在堆上分配内存,随着堆的增长,会从内核请求越来越多的内存。 如果所有的物理内存都耗尽,最终可能会导致交换失败。 一旦交换空间耗尽,随机进程将被内核的OOM杀手杀死。
但请注意,内存分配可能会因为缺少空闲内存,碎片或达到配置的限制等原因而失败。 glib分配器使用的sbrk()
和mmap()
调用有自己的失败,例如程序中断到达另外一个已经分配的地址(例如共享内存或先前用mmap()
映射的页)或进程的最大内存映射数已经超过。
我认为你最初的问题是, malloc
未能在你的系统上分配请求的内存。
为什么发生的是特定于您的系统。
当一个进程被加载时,它被分配内存达到一个特定的地址,这是进程的系统中断点。 除了这个地址之外,这个进程的内存还没有被映射。 所以当进程“击中”“休息”点时,它会向系统请求更多的内存,一种方法是通过系统调用sbrk
malloc
可以做到这一点,但在你的系统出于某种原因,它失败了。
这可能有很多原因,例如:
1)我认为在Linux中,最大内存大小是有限制的。 我认为这是ulimit
,也许你打了。 检查是否设置了限制
2)也许你的系统太重了
3)你的程序做了不好的内存管理,最终导致内存碎裂,所以malloc
无法获得你所要求的块大小。
4)你的程序破坏了malloc
内部数据结构,即坏指针的使用
等等