分配内存如何以及为何失败?

Flo*_*oux 7 c++ memory memory-management

这是我在学生时问自己的问题,但未能得到令人满意的答案,我一点一点地想到了......直到今天.

我知道我能应付分配内存错误或者通过检查,如果返回的指针为NULL或处理bad_alloc异常.

好的,但我想知道:的召唤怎么以及为什么会失败?据我所知,如果免费商店中没有足够的空间,分配内存可能会失败.但是,这是否真的情况发生时下,有几个GB的RAM(至少在普通电脑上,我说的不是嵌入式系统)?我们可以在其他情况下发生分配内存故障吗?

Jer*_*fin 16

虽然你已经得到了一些关于为什么/如何记忆的答案可能会失败,他们大多是那种无视现实的.

实际上,在实际系统中,大多数这些论点都没有描述事物是如何工作的.虽然他们是对从视点,这些都是原因未遂内存分配可能会失败,他们大多是从描述事情是如何去通常在现实工作的观点是错误的.

例如,在Linux中,如果您尝试分配的内存多于系统可用的内存,则分配不会失败(即,您不会获得空指针或strd :: bad_alloc异常).相反,系统将"过度提交",因此您获得看似有效的指针 - 但是当/如果您尝试使用所有内存,您将获得异常,和/或OOM Killer将运行,试图通过杀死使用大量内存的进程来释放内存.不幸的是,这可能很容易杀死作为其他程序发出请求的程序(事实上,许多给出的尝试通过重复分配大块内存导致分配失败的例子应该是最先被杀死的).

Windows上运行一点点接近C和C++标准是如何设想的东西(但只有一点点).Windows通常配置为在必要时扩展交换文件以满足内存分配请求.这意味着,当您分配更多内存时,系统会因交换内存而半疯狂,创建越来越大的交换文件以满足您的请求.

这最终会失败,但是在一个拥有大量驱动器空间的系统上,它可能会运行几个小时(其中大部分都会在磁盘上乱码).至少在一个典型的客户端机器上,用户实际上......好吧,使用计算机,他会注意到所有东西都拖延到了停止状态,并且在分配失败之前做了一些事情来阻止它.

因此,要获得真正失败的内存分配,您通常会寻找除典型桌面计算机之外的其他内容.一些示例包括一次无人值守运行数周的服务器,并且负载太轻以至于没有人注意到它正在颠倒磁盘12小时,或者是运行MS-DOS的机器或者没有运行MS-DOS的计算机提供虚拟内存.

一句话:你基本上是正确的,他们基本上是错的.虽然这是千真万确的,如果你分配比该机支持更多的内存,那东西的得给,它通常不是真实的,失败必然由C++标准规定的方式发生-而且,事实上,对于典型的台式机这更像是例外(赦免双关语)而不是规则.


Mat*_*lia 6

通常在现代机器上它会由于虚拟地址空间的稀缺而失败;如果您有一个 32 位进程尝试分配超过 2/3 GB 的内存1,即使有物理 RAM(或分页文件)来满足分配,虚拟地址空间中也不会有空间映射此类新分配的内存。

当虚拟地址空间严重碎片化时,会发生另一种(类似的)情况,因此分配失败,因为没有足够的连续地址。

另外,内存不足也可能发生,事实上我上周就遇到过这种情况;但在这种情况下,一些操作系统(尤其是 Linux)不会返回 NULL:Linux 会很乐意为您提供一个指向尚未提交的内存区域的指针,并在程序尝试写入时实际分配它;如果此时没有足够的内存,内核将尝试杀死一些占用内存的进程以释放内存(此行为的一个例外似乎是当您尝试分配超过 RAM 和交换分区的全部容量时- 在这种情况下,您会得到NULL预付款)。

从 malloc 获取 NULL 的另一个原因可能是操作系统对进程强制执行的限制;例如,尝试运行此代码

#include <cstdlib>
#include <iostream>
#include <limits>

void mallocbsearch(std::size_t lower, std::size_t upper)
{
    std::cout<<"["<<lower<<", "<<upper<<"]\n";
    if(upper-lower<=1)
    {
        std::cout<<"Found! "<<lower<<"\n";
        return;
    }
    std::size_t mid=lower+(upper-lower)/2;
    void *ptr=std::malloc(mid);
    if(ptr)
    {
        free(ptr);
        mallocbsearch(mid, upper);
    }
    else
        mallocbsearch(lower, mid);
}

int main()
{
    mallocbsearch(0, std::numeric_limits<std::size_t>::max());
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在 Ideone 上,setrlimit您会发现最大分配大小约为 530 MB,这可能是(Windows 上存在类似机制)强制执行的限制。


  1. 它因操作系统而异,并且通常可以配置;32 位进程的总虚拟地址空间为 4 GB,但在当前所有主流操作系统上,其中很大一部分(默认设置的 32 位 Windows 上的 2 GB)被保留用于内核数据。


Tho*_*mas 6

除了明显的"内存不足"之外,内存碎片也可能导致这种情况.想象一下执行以下操作的程序:

  • 直到主内存几乎满了:
    • 分配1020个字节
    • 分配4个字节
  • 释放所有1020字节块

如果内存管理器按顺序将所有这些顺序放入内存中,我们现在有足够的可用内存,但任何大于1020字节的分配都无法找到连续的空间来放置它们,并且会失败.