我正在写一些代码而且它一直在崩溃。在挖掘转储后,我意识到我超出了最大堆限制(如果我在malloc上添加了检查,生活会更容易)。虽然我修复了,有没有办法增加我的堆大小?
PS:这里有similar question,但回复对我来说不清楚。
答案 0 :(得分:15)
堆和内存管理是您的C库(可能是glibc)提供的工具。每次执行malloc()
时,它都会保留堆并返回内存块。它不知道堆大小限制:每次请求的内存多于堆上可用的内存时,它只会向内核请求更多内容(使用sbrk()
或mmap()
)。
默认情况下,内核在询问时几乎总会给你更多内存。这意味着malloc()
将始终返回有效地址。只有在您第一次引用已分配的页面时,内核才会真正为您找到一个页面。如果它发现它无法给你一个它运行一个OOM杀手,根据某种称为 badness (包括你的进程及其子的虚拟内存大小,漂亮的级别,整体运行时间等)选择一个受害者并发送SIGTERM
。这种内存管理技术称为overcommit,内核在/proc/sys/vm/overcommit_memory
为0或1时使用。有关详细信息,请参阅内核文档中的overcommit-accounting。
通过将2写入/proc/sys/vm/overcommit_memory
,您可以禁用过度使用。如果你这样做,内核将在承诺之前实际检查它是否有内存。如果没有更多可用内存,这将导致malloc()
返回NULL。
您还可以使用setrlimit()
和RLIMIT_AS
或ulimit -v
命令为进程分配的虚拟内存设置限制。无论上面描述的overcommit设置如何,如果进程尝试分配的内存超过限制,内核将拒绝它,malloc()
将返回NULL。请注意,与现代Linux内核(包括整个2.6.x系列)相比,驻留大小(setrlimit()
与RLIMIT_RSS
或ulimit -m
命令)的限制无效。
下面的会话是在内核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()
映射的页面)或进程的最大内存映射数已超出。
答案 1 :(得分:9)
堆通常与架构上的可寻址虚拟内存一样大。
您应该使用ulimit -a
命令检查系统当前限制并查找此行max memory size (kbytes, -m) 3008828
,我的OpenSuse 11.4 x86_64上的此行与~3.5 GiB的ram说我的每个RAM约为3GB过程
然后,您可以使用这个简单的程序真正测试您的系统,以检查每个进程的最大可用内存:
#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并显示最终可用的最大内存量。
警告是你的系统将在最后阶段开始大量交换内存。根据您的系统配置,内核可能会决定终止某些进程。我使用100 MiB增量,因此某些应用程序和系统有一些喘息空间。你应该关闭任何你不想崩溃的东西。
话虽如此。在我写的系统中,没有任何事情发生过。上面的程序报告与ulimit -a
几乎没有相同。不同之处在于它实际测试了内存,并通过memset()
确认了内存的使用和使用。
为了在具有256 MiB ram和400MiB交换的Ubuntu 10.04x86 VM上进行比较,ulimit报告为memory size (kbytes, -m) unlimited
,我的小程序报告了524.288.000字节,这大致是组合ram和swap,折扣ram其他人使用的软件和内核。
编辑:正如Adam Zalcman写的那样,ulimit -m
已经不再适用于较新的2.6及更高版本的Linux内核,所以我有所纠正。但是ulimit -v
很荣幸。为了获得实际结果,您应该将-m替换为-v,并查找virtual memory (kbytes, -v) 4515440
。似乎我的suse盒子的-m值与我的小实用程序报告的值一致。你应该记住,这是由内核分配的虚拟内存,如果物理内存不足,则需要交换空间来弥补它。
如果您想知道有多少物理ram可用而不会干扰任何过程或系统,您可以使用
long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;
这将排除缓存和缓冲区内存,因此该数字可能远小于实际可用内存。操作系统缓存可以很安静,它们的驱逐可以提供所需的额外内存,但这是由内核处理的。
答案 2 :(得分:2)
我认为你原来的问题是malloc
无法在你的系统上分配所请求的内存。
为什么发生这种情况特定于您的系统。
加载进程时,会将内存分配给某个地址,该地址是进程的系统断点。除了该地址之外,该进程的内存未被映射。因此,当流程“点击”“中断”点时,它会从系统请求更多内存,一种方法是通过系统调用sbrk
malloc
会在引擎盖下执行此操作,但在您的系统中由于某种原因它失败了。
例如,可能有很多原因:
1)我认为在Linux中存在最大内存大小的限制。我认为这是ulimit
,也许你就是这样。检查是否设置为限制
2)也许你的系统太负载了
3)您的程序执行了错误的内存管理,并最终得到了内存,因此malloc
无法获得您请求的块大小。
4)您的程序破坏了malloc
内部数据结构,即错误的指针使用
等等
答案 3 :(得分:0)
我想在以前的答案中加一分。
应用程序有幻觉,即malloc()返回“ solid”块;实际上,在RAM的许多页上可能分散分散地存在一个缓冲区。这里的关键事实是:进程的虚拟内存(包含其代码或包含大数组的内容)必须是连续的。我们甚至承认代码和数据是分开的。大数组char str [universe_size]必须是连续的。
现在:单个应用程序可以任意扩展堆以分配这样的数组吗?
如果机器上没有其他东西在运行,答案可能是“是”。堆可能非常大,但是必须有边界。在某些时候,对sbrk()的调用(在Linux中,简称为“扩大”堆的函数)应该在为另一个应用程序保留的区域上迷路了。
此link提供了一些有趣且清晰的示例,请查看。我在Linux上找不到该信息。
答案 4 :(得分:0)
您可以从顶部找到webapp / java进程的进程ID。 使用jmap heap-获取堆分配。我在AWS-Ec2上测试了弹性beantalk,并给出了分配的堆。这是详细的答案 Xmx settings in elasticbean stalk through environment properties