分配更多的内存比使用malloc存在
这个代码片段每次从stdin中读取字母“u”时将分配2Gb,并且在读取“a”时将初始化所有分配的字符。
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <vector> #define bytes 2147483648 using namespace std; int main() { char input [1]; vector<char *> activate; while(input[0] != 'q') { gets (input); if(input[0] == 'u') { char *m = (char*)malloc(bytes); if(m == NULL) cout << "cant allocate mem" << endl; else cout << "ok" << endl; activate.push_back(m); } else if(input[0] == 'a') { for(int x = 0; x < activate.size(); x++) { char *m; m = activate[x]; for(unsigned x = 0; x < bytes; x++) { m[x] = 'a'; } } } } return 0; }
我在具有RAM的3Gb的Linux虚拟机上运行此代码。 在使用htop工具监视系统资源使用情况的同时,我意识到malloc操作并不反映在资源上。
例如,当我input“u”只有一次(即分配2GB的堆内存),我没有看到在htop内存使用增加2GB。 只有当我input'a'(即初始化),我看到内存使用量增加。
因此,我能够比现有的“malloc”更多的堆内存。 例如,我可以malloc 6GB(这是比我的内存和交换内存),malloc将允许它(即NULL不返回malloc)。 但是当我尝试初始化分配的内存时,我可以看到内存和交换内存被填满,直到进程被终止。
– 我的问题:
这是一个内核错误吗?
2.有人向我解释为什么这种行为是被允许的?
这被称为内存过量使用 。 您可以通过以root身份运行来禁用它:
echo 2 > /proc/sys/vm/overcommit_memory
这不是我喜欢的内核function(所以我总是禁用它)。 请参阅malloc(3)和mmap(2)和proc(5)
NB: 经常 echo 0
而不是echo 2
但并不总是有效。 阅读文档(特别是我刚刚链接的proc
手册页)。
从man malloc
( 在线在线 ):
默认情况下,Linux遵循乐观的内存分配策略。 这意味着当malloc()返回非NULL时,不能保证内存真的可用。
所以当你只是想分配太多时,对你来说是“谎言”,当你想使用分配的内存时,它会尝试为你find足够的内存,如果找不到足够的内存,它可能会崩溃。
不,这不是内核错误。 您已经发现了一种称为晚分页(或过分分配)的情况。
在你写一个字节给malloc (...)
分配的地址之前,内核只是“保留”地址范围。 这当然取决于你的内存分配器和操作系统的实现,但是大多数好的内核直到内存第一次使用才会产生大部分的内核开销。
囤积分配器是一个突然想到的大罪犯,通过广泛的testing,我发现它几乎从来没有利用支持晚分页的内核。 如果您在分配之后立即对整个内存范围进行零填充,则可以始终减轻任何分配器中的晚分页的影响。
像VxWorks这样的实时操作系统永远不会允许这种行为,因为延迟分页引起严重的延迟。 从技术上讲,它所做的只是将延迟closures,直到后来的不确定时间。
有关更详细的讨论,您可能有兴趣了解IBM的AIX操作系统如何处理页面分配和过度使用 。
这是巴西尔提到的,超过提交记忆的结果。 不过,这种解释有趣。
基本上,当你尝试在Linux(POSIX?)中映射额外的内存时,内核将保留它,并且只有当你的应用程序访问其中一个保留的页面时,才会真正使用它。 这允许多个应用程序保留超过实际的RAM /交换总量。
在大多数Linux环境中,这是可取的行为,除非你有一个实时的操作系统,或者你知道谁将需要什么资源,何时和为什么。
否则,有人可以走,malloc所有的内存(没有实际做任何事情)和OOM你的应用程序。
这个懒惰分配的另一个例子是mmap(),在那里你有一个虚拟地图,你映射的文件可以放在里面 – 但是你只有less量的真实内存用于这个工作。 这允许你mmap()巨大的文件(大于你的可用RAM),并使用它们像正常的文件句柄是漂亮的)
-n
初始化/使用内存应该工作:
memset(m, 0, bytes);
你也可以使用calloc
不仅分配内存,而且还为你填充零:
char* m = (char*) calloc(1, bytes);
这是一个内核错误吗?
没有。
2.有人向我解释为什么这种行为是被允许的?
有几个原因:
-
减轻需要知道最终的内存需求 – 让一个应用程序能够拥有一定数量的内存通常是方便的,它认为这个内存的实际需要是一个上限。 例如,如果准备某种报告,只是为了计算报告的最终大小,或者连续地更大面积(有复制风险)的realloc()会使代码显着复杂化,并且会影响性能,其中,每个条目的最大长度乘以条目的数量可能非常快速和容易。 如果你知道虚拟内存相对于你的应用程序的需求来说是相当丰富的,那么做一个更大的虚拟地址空间分配是非常便宜的。
-
稀疏数据 – 如果你有虚拟地址空间备用,能够有一个稀疏数组并且使用直接索引,或者分配一个大容量()到大小()比率的散列表,可以导致一个非常高的性能系统。 当数据元素大小是内存分页大小的倍数,或者失败那么大或者小的整数部分时,两者都工作得最好(在具有低开销/浪费和有效使用内存高速caching的意义上)。
-
资源共享 – 考虑一家ISP为一栋build筑中的1000名消费者提供“1千兆比特每秒”的连接 – 他们知道,如果所有消费者同时使用它,他们将获得大约1兆比特,尽pipe人们要求1千兆比特,并且在特定的时间需要一小部分,但是并发使用的平均值却不可避免地会有一些较低的最大值和较低的平均值。 应用于内存的相同洞察力允许操作系统支持比其他方式更多的应用程序,并在合理的平均成功的情况下满足期望。 就像共享互联网连接速度随着更多用户同时请求而降低一样,从磁盘上的交换内存进行分页可能会引发并降低性能。 但与互联网连接不同,交换内存是有限制的,如果所有的应用程序真的试图同时使用内存,超过这个限制,一些会开始获取信号/中断/陷阱报告内存耗尽。 总而言之,在启用这种内存过量使用行为的情况下,只要检查
malloc()
/new
返回的非NULL指针是不足以保证物理内存实际可用的,并且程序在尝试使用内存时可能还会收到一个信号。