简体   繁体   English

使用 malloc 分配比现有内存更多的内存

[英]Allocating more memory than there exists using malloc

This code snippet will allocate 2Gb every time it reads the letter 'u' from stdin, and will initialize all the allocated chars once it reads 'a'.此代码片段每次从 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;
}

I am running this code on a linux virtual machine that has 3Gb of ram.我在具有 3Gb ram 的 linux 虚拟机上运行此代码。 While monitoring the system resource usage using the htop tool, I have realized that the malloc operation is not reflected on the resources.在使用htop工具监控系统资源使用情况时,发现malloc操作并没有反映在资源上。

For example when I input 'u' only once(ie allocate 2GB of heap memory), I don't see the memory usage increasing by 2GB in htop.例如,当我只输入 'u' 一次(即分配 2GB 的堆内存)时,我没有看到 htop 中的内存使用量增加了 2GB。 It is only when I input 'a'(ie initialize), I see the memory usage increasing.只有当我输入'a'(即初始化)时,我才看到内存使用量增加。

As a consequence, I am able to "malloc" more heap memory than there exists.因此,我能够“分配”比现有更多的堆内存。 For example, I can malloc 6GB(which is more than my ram and swap memory) and malloc would allow it(ie NULL is not returned by malloc).例如,我可以 malloc 6GB(这比我的 ram 和交换内存多)并且 malloc 会允许它(即 malloc 不返回 NULL)。 But when I try to initialize the allocated memory, I can see the memory and swap memory filling up till the process is killed.但是当我尝试初始化分配的内存时,我可以看到内存和交换内存被填满,直到进程被终止。

-My questions: - 我的问题:

1.Is this a kernel bug? 1.这是内核错误吗?

2.Can someone explain to me why this behavior is allowed? 2.有人可以向我解释为什么允许这种行为吗?

It is called memory overcommit .这称为内存过量使用 You can disable it by running as root:您可以通过以 root 身份运行来禁用它:

 echo 2 > /proc/sys/vm/overcommit_memory

and it is not a kernel feature that I like (so I always disable it).它不是我喜欢的内核功能(所以我总是禁用它)。 See malloc(3) and mmap(2) and proc(5)参见malloc(3)mmap(2)proc(5)

NB: echo 0 instead of echo 2 often -but not always- works also.注意: echo 0而不是echo 2通常- 但并非总是 - 也有效。 Read the docs (in particular proc man page that I just linked to).阅读文档(特别是我刚刚链接到的proc手册页)。

from man malloc ( online here ):来自man malloc在这里在线):

By default, Linux follows an optimistic memory allocation strategy.默认情况下,Linux 遵循乐观内存分配策略。 This means that when malloc() returns non-NULL there is no guarantee that the memory really is available.这意味着当 malloc() 返回非 NULL 时,不能保证内存确实可用。

So when you just want to allocate too much, it "lies" to you, when you want to use the allocated memory, it will try to find enough memory for you and it might crash if it can't find enough memory.因此,当您只想分配太多内存时,它“对您说谎”,当您想使用分配的内存时,它会尝试为您找到足够的内存,如果找不到足够的内存,它可能会崩溃。

No, this is not a kernel bug.不,这不是内核错误。 You have discovered something known as late paging (or overcommit).您发现了一种称为延迟分页(或过量使用)的东西。

Until you write a byte to the address allocated with malloc (...) the kernel does little more than "reserve" the address range.在您向使用malloc (...)分配的地址写入一个字节之前,内核所做的只是“保留”地址范围。 This really depends on the implementation of your memory allocator and operating system of course, but most good ones do not incur the majority of kernel overhead until the memory is first used.当然,这确实取决于内存分配器和操作系统的实现,但大多数好的内存分配器在首次使用内存之前不会产生大部分内核开销。

The hoard allocator is one big offender that comes to mind immediately, through extensive testing I have found it almost never takes advantage of a kernel that supports late paging.囤积分配器是一个立即想到的大罪犯,通过广泛的测试,我发现它几乎从不利用支持延迟分页的内核。 You can always mitigate the effects of late paging in any allocator if you zero-fill the entire memory range immediately after allocation.如果在分配后立即将整个内存范围填零,则始终可以减轻任何分配器中后期分页的影响。

Real-time operating systems like VxWorks will never allow this behavior because late paging introduces serious latency.像 VxWorks 这样的实时操作系统永远不会允许这种行为,因为延迟分页会导致严重的延迟。 Technically, all it does is put the latency off until a later indeterminate time.从技术上讲,它所做的只是将延迟推迟到以后的不确定时间。

For a more detailed discussion, you may be interested to see how IBM's AIX operating system handles page allocation and overcommitment .有关更详细的讨论,您可能有兴趣了解 IBM 的 AIX 操作系统如何处理页面分配过度使用

This is a result of what Basile mentioned, over commit memory.这是 Basile 提到的过度提交内存的结果。 However, the explanation kind of interesting.然而,解释有点有趣。

Basically when you attempt to map additional memory in Linux (POSIX?), the kernel will just reserve it, and will only actually end up using it if your application accesses one of the reserved pages.基本上,当您尝试在 Linux (POSIX?) 中映射额外的内存时,内核只会保留它,并且只有在您的应用程序访问保留页面之一时才会实际使用它。 This allows multiple applications to reserve more than the actual total amount of ram / swap.这允许多个应用程序保留比实际总量更多的 ram/swap。

This is desirable behavior on most Linux environments unless you've got a real-time OS or something where you know exactly who will need what resources, when and why.这在大多数 Linux 环境中都是可取的行为,除非您有一个实时操作系统或您确切知道谁需要什么资源、何时以及为什么需要的东西。

Otherwise somebody could come along, malloc up all the ram (without actually doing anything with it) and OOM your apps.否则有人可能会出现,将所有内存分配给内存(实际上并没有对其进行任何操作)并 OOM 应用程序。

Another example of this lazy allocation is mmap(), where you have a virtual map that the file you're mapping can fit inside - but you only have a small amount of real memory dedicated to the effort.这种懒惰分配的另一个例子是 mmap(),其中您有一个虚拟映射,您正在映射的文件可以放入其中 - 但您只有少量的实际内存专用于这项工作。 This allows you to mmap() huge files (larger than your available RAM), and use them like normal file handles which is nifty)这允许您 mmap() 大文件(大于可用 RAM),并像使用普通文件句柄一样使用它们,这很漂亮)

-n -n

Initializing / working with the memory should work:初始化/使用内存应该可以工作:

memset(m, 0, bytes);

Also you could use calloc that not only allocates memory but also fills it with zeros for you:您也可以使用calloc ,它不仅可以分配内存,还可以为您填充零:

char* m = (char*) calloc(1, bytes);

1.Is this a kernel bug? 1.这是内核错误吗?

No.不。

2.Can someone explain to me why this behavior is allowed? 2.有人可以向我解释为什么允许这种行为吗?

There are a few reasons:有几个原因:

  • Mitigate need to know eventual memory requirement - it's often convenient to have an application be able to an amount of memory that it considers an upper limit on the need it might actually have.减轻了解最终内存需求的需要- 让应用程序能够使用它认为可能实际需要的上限的内存量通常很方便。 For example, if it's preparing some kind of report either of an initial pass just to calculate the eventual size of the report or a realloc() of successively larger areas (with the risk of having to copy) may significantly complicate the code and hurt performance, where-as multiplying some maximum length of each entry by the number of entries could be very quick and easy.例如,如果它正在准备某种类型的报告,无论是初始传递只是为了计算报告的最终大小,还是连续较大区域的 realloc()(具有必须复制的风险)可能会显着使代码复杂化并损害性能,其中将每个条目的某个最大长度乘以条目数可能非常快速和容易。 If you know virtual memory is relatively plentiful as far as your application's needs are concerned, then making a larger allocation of virtual address space is very cheap.如果您知道就您的应用程序的需求而言,虚拟内存相对充足,那么分配更大的虚拟地址空间是非常便宜的。

  • Sparse data - if you have the virtual address space spare, being able to have a sparse array and use direct indexing, or allocate a hash table with generous capacity() to size() ratio, can lead to a very high performance system.稀疏数据——如果你有空闲的虚拟地址空间,能够有一个稀疏数组并使用直接索引,或者分配一个容量()与大小()比率很大的哈希表,可以导致一个非常高性能的系统。 Both work best (in the sense of having low overheads/waste and efficient use of memory caches) when the data element size is a multiple of the memory paging size, or failing that much larger or a small integral fraction thereof.当数据元素大小是内存分页大小的倍数或失败时,两者都工作得最好(在具有低开销/浪费和有效使用内存缓存的意义上)。

  • Resource sharing - consider an ISP offering a "1 giga-bit per second" connection to 1000 consumers in a building - they know that if all the consumers use it simultaneously they'll get about 1 mega-bit, but rely on their real-world experience that, though people ask for 1 giga-bit and want a good fraction of it at specific times, there's inevitably some lower maximum and much lower average for concurrent usage.资源共享- 考虑一个 ISP 为建筑物中的 1000 名消费者提供“每秒 1 千兆位”的连接 - 他们知道如果所有消费者同时使用它,他们将获得大约 1 兆位,但依赖于他们的实际 -世界经验表明,尽管人们要求 1 Gb 并希望在特定时间获得很大一部分,但不可避免地存在一些较低的最大值和并发使用的平均值低得多的情况。 The same insight applied to memory allows operating systems to support more applications than they otherwise would, with reasonable average success at satisfying expectations.应用于内存的相同见解允许操作系统支持比其他方式更多的应用程序,并在满足预期方面取得合理的平均成功。 Much as the shared Internet connection degrades in speed as more users make simultaneous demands, paging from swap memory on disk may kick in and reduce performance.与共享 Internet 连接的速度随着更多用户同时提出需求而降低一样,从磁盘上的交换内存进行分页可能会启动并降低性能。 But unlike an internet connection, there's a limit to the swap memory, and if all the apps really do try to use the memory concurrently such that that limit's exceeded, some will start getting signals/interrupts/traps reporting memory exhaustion.但与 Internet 连接不同的是,交换内存是有限制的,如果所有应用程序确实尝试同时使用内存以致超出该限制,则有些应用程序将开始收到报告内存耗尽的信号/中断/陷阱。 Summarily, with this memory overcommit behaviour enabled, simply checking malloc() / new returned a non-NULL pointer is not sufficient to guarantee the physical memory is actually available, and the program may still receive a signal later as it attempts to use the memory.总而言之,在启用这种内存过量使用的情况下,仅检查malloc() / new返回的非空指针不足以保证物理内存实际可用,并且程序稍后在尝试使用内存时可能仍会收到信号.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM