C语言中的malloc()函数和内存使用

4

我正在尝试使用malloc进行实验,以查看是否可以分配所有可用的内存。

我使用了以下简单的程序,并有一些问题:

int main(void)
{
    char * ptr;
    int x = 100;

    while(1)
    {
        ptr = (char *) malloc(x++ * sizeof(char) / 2);
        printf("%p\n",ptr);
    }

    return 0;
}

1) 为什么使用更大的数据类型(int、unsigned long long int、long double)时,进程会使用更少的内存,而使用更小的数据类型(int、char)时,它会使用更多的内存?

2) 运行程序时,当达到一定数量的内存(在 Windows 7 64 位上约为 592mb,具有 8GB RAM 交换文件设置为系统管理)。如果打印输出显示为 0,这意味着 NULL。为什么在达到此阈值后停止分配内存,而不是耗尽系统内存和交换空间?

我发现以下帖子中有人尝试与我做同样的事情,但他们没有看到内存使用方面的任何差异,而我看到了。 Memory Leak Using malloc fails

我已在 Linux 内核 2.6.32-5-686 上尝试了代码,并获得了类似的结果。

感谢您的帮助和解释。

谢谢,


在别人说之前,我想说一句话,不要强制转换malloc的返回值。噢噢噢!! - user681007
本地堆和系统堆是有区别的。在Windows中,应用程序无法获得很大的本地堆,因此malloc会更快地耗尽内存。使用VirtualAlloc()从系统堆中进行分配。那么问题可能变成小分配的句柄/页面不足而非实际内存不足。 - BitBank
为什么不呢?你有什么建议吗? - bang
不要将 malloc 的返回值强制转换。这样做充其量是多余的...而且,就像在您的情况下一样,可能会隐藏编译器可以在没有强制转换的情况下捕获的错误。 - pmg
@bang和ricola86:如果您需要转换malloc的返回值,那么您并不是在编译C,而是在编译C++。如果您正在编译C ++,为什么要使用malloc,而不是应该使用new?这就是原因。 - Zan Lynx
显示剩余4条评论
4个回答

6

1)通常情况下,内存是按页的倍数分配的,因此如果您请求的大小小于一页,则malloc将至少分配一页。

2)这是有道理的,因为在多任务系统中,您不是唯一的用户,您的进程也不是唯一运行的进程,还有许多其他进程共享有限的资源,包括内存。如果操作系统允许一个进程无限制地分配所需的所有内存,那么它就不是一个好的操作系统,对吧?

最后,在Linux中,内核直到您实际开始使用此内存之前才会分配任何物理内存页,因此仅调用malloc并不会实际消耗任何物理内存,当然除了需要跟踪分配本身所需的内存之外。但我不确定Windows是否也是这样。

编辑:以下示例分配1GB虚拟内存。

#include <stdio.h>
int main(int agrc, char **argv)
{
    void *p = malloc(1024*1024*1024);
    getc(stdin);
}

如果您运行 top 命令,您将获得以下内容:
top -p `pgrep test`
PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
20   0 1027m  328  252 S    0  0.0   0:00.00 test
如果你将malloc更改为calloc,并再次运行top,你会得到:
top -p `pgrep test`
PR   NI VIRT  RES  SHR S %CPU %MEM    TIME+ COMMAND              
20   0  1027m 1.0g 328 S    0  1.3   0:00.08 test

你对第一点和第二点的解释让我更清楚了。然而,当我在Linux下运行代码并运行free命令时,可用内存的数量减少了。 - maxmouse
@user1216551 我不确定,但我认为这可能与跟踪分配本身有关的开销有关,我已经在我的答案中添加了一个示例。 - iabdalkader
当运行 top 命令时,我的代码会生成如下输出: PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 20 0 3071m 427m 328 R 44.3 85.0 0:11.84 malloc - maxmouse
@user1216551,我认为这看起来很合理。如果malloc必须实际跟踪所有这些分配及其大小,我在这里运行了您的代码,并达到了vert/res:10.3g / 804m。 - iabdalkader

1
你是如何读取内存使用情况的?
1)当使用char进行分配时,每次分配的内存比例如long(通常为四分之一,但这取决于机器)少。由于大多数程序外部的内存使用工具不显示已分配的内存,而实际上显示的是已使用的内存,因此它只会显示malloc()本身使用的开销,而不是您malloc的未使用内存。
更多的分配,意味着更多的开销。
如果您为每个分配填充malloc的块以使内存实际被使用,则应该得到非常不同的结果。
2)我假设您是从同一个工具中读取的?尝试计算您实际分配了多少字节,它应该显示正确的数量,而不仅仅是“malloc开销”。

我在Windows中使用任务管理器查看内存使用情况。你的解释很有道理。当我向malloc分配的块填充数据时,程序几乎立即崩溃。 - maxmouse

1

1) 当你分配内存时,每个分配都需要请求的内存空间加上堆栈帧的大小。请参见相关问题这里

2) 在Windows中,任何单个malloc的大小都受到_HEAP_MAXREQ的限制。有关更多信息和一些解决方法,请参见此问题


-1

1) 这可能是因为内存是分页的,每个页面的大小相同。如果你的数据无法放入一页中,而是落在两个页面之间,我认为它将被移动到下一页的开头,从而在前一页末尾创建空间损失。

2) 阈值较小,因为我认为每个程序都受限于一定数量的数据,而不是您拥有的总最大内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接