为了测试内存不足行为,我使用GCC 4.7.1在32位Linux 3.2上编译了以下C程序,没有任何编译器标志:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
while (malloc(4096)) ;
printf("%s", strerror(errno));
return 0;
}
当我运行程序时,我发现在分配了大约2.5 GB的驻留内存后,malloc失败了(“无法分配内存”)。
该机器具有2GB的物理内存和4GB的交换空间。在程序运行期间没有观察到内核消息。
那么为什么Linux会停止发布内存呢?
相关问题:maximum memory which malloc can allocate,但它没有涉及Linux细节。
答案 0 :(得分:9)
机器中的物理内存量与malloc
的语义无关。进程具有固定的虚拟地址空间(对于32位进程通常为2GB)。 malloc
的实现将返回addreses,直到它用完虚拟地址空间而不是物理RAM。
这是一个更详细的讨论
答案 1 :(得分:4)
您正在观察的是VM溢出。
32位进程只能访问4GB的虚拟地址空间。 该虚拟地址空间必须适应
操作系统通常会对CPU上的内存控制器进行编程,为内核页面(其中包括vsyscalls)保留(大)部分VM空间,并将进程可用的内存限制为小于4GB。这曾经是VM的一半,现在它已经少了。
glibc
malloc
的实现为您的堆保留了一个(或多个)VM空间块,但它被编程为为共享映射留下足够的VM空间。
因此,根据虚拟地址空间中的其他内容,您可能会在整个VM饱和之前“内存不足”。
请咨询/proc/[pid]/maps
以获取VM映射的位置。
使用完整虚拟机的唯一方法是使用mmap
和一系列调用在您的免费虚拟机中的任何位置分配内存。尝试:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
int main() {
char * page;
size_t memory_allocated = 0;
while ((page = mmap(NULL, sysconf(_SC_PAGE_SIZE)
, PROT_READ | PROT_WRITE
, MAP_ANON | MAP_PRIVATE, -1, 0))
!= MAP_FAILED) {
memory_allocated += sysconf(_SC_PAGE_SIZE);
// optionally touch the page
// otherwise the mapping won't use physical RAM/swap
// *page = 1;
}
fprintf(stderr, "Allocated %zu MiB\n", memory_allocated >> 20);
perror("mmap");
return 0;
}
请不要尝试将其作为64位二进制文件,因为它会尝试在那里使用高达256TB的RAM。
在我的Mac OS X上,我得到:
$ gcc -m32 17935873.c
$ ./a.out
Allocated 3516 MiB
mmap: Cannot allocate memory
答案 2 :(得分:0)
您是否使用root
权限运行此代码?一旦我尝试了具有user
权限的内存分配代码,我就会收到错误。
这不是你的答案,但我想在这里分享我的示例代码,也许我们可以从中学到一些东西。使用这种方法,我们可以分配所需的内存量,直到内存耗尽。这是有效的代码(如果你尝试了更多的内存,那么它会给出错误)。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define A_MEGABYTE (1024 * 1024)
#define PHY_MEM_MEGS 1024 /* Adjust this number as required */
int main()
{
char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
int megs_obtained = 0;
while (megs_obtained < (PHY_MEM_MEGS * 2))
{
some_memory = (char *)malloc(size_to_allocate);
if (some_memory != NULL)
{
megs_obtained++;
sprintf(some_memory, “Hello World”); //Message to check
printf(“%s - now allocated %d Megabytes\n”, some_memory, megs_obtained);
}
else
{
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
}
可能会有所帮助。
EDIT
这取决于我的理解,如果我错了,请告诉我。
正如你所说,你得到2.5 GB
内存,这是因为堆可以分配2/3 times
的内存。因此,您将从Max 3 GB
内存获得4 GB
。 Rest memory occupied by some processes and definitely process scheduler
。没有智能操作系统允许自杀。
我在这里阅读了Operating System
Galvin
本书。根据你的案例中的书,如果你想分配更多的内存,只需要调用malloc
1000次(每个malloc调用一行),或者直到操作系统的Page Size
。直到你在内存中交换页面,你才会得到Kernel Message
(理论上)。你的页面没有从内存中传出,这是唯一的问题。
如果您要检查所有Swap area
,请使用Thread
sleep
编写此代码,以便检查交换内存中的所有进程。
我没有写任何代码,所以如果你写一些代码请更新我们。
答案 3 :(得分:0)
内存可能会碎片化,因此有几个小于4k的空间块。 malloc只有在找到所请求的空间时才会返回指向已分配内存的指针。
此外,在未分配的存储器中存在嵌入指针,其指向或链接在一起的空闲区域(每个K&amp; R)。这是一个适用于Linux和Windows的一般声明。
在Windows中,我已经转储了malloc返回的指针周围的区域,发现Windows有大约16个字节的信息(我认为主要是句柄)为每个malloc'd区域添加前缀。这些前缀不会返回给malloc的调用者,但它们会占用空间并减少特定计算机上可用的区域。
您可以尝试使用第二个循环来降低分配:
int main()
{
int avail = 4096;
while (malloc(avail)) ;
printf("%s", strerror(errno));
avail = avail >> 1;
while (malloc(avail)) ;
printf("%s", strerror(errno));
// etc. down to whatever granularity you want
// or even better code an inner lup to do this
return 0;
}