繁体   English   中英

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

[英]How and why an allocation memory can fail?

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

我知道我可以通过检查返回的指针是否为NULL或处理bad_alloc异常来处理分配内存错误。

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

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

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

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

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

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

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

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

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

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

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

通常在现代机器上,由于虚拟地址空间的稀缺,它会失败; 如果你有一个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;
}

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


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

给定进程可用的内存量是有限的。 如果进程耗尽其内存并尝试分配更多内存,则分配将失败。

分配可能失败的原因还有其他原因。 例如,堆可能会碎片化,并且没有足够大的单个空闲块来满足分配请求。

暂无
暂无

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

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