代码之家  ›  专栏  ›  技术社区  ›  Walter Bright

“内存不足”是可恢复的错误吗?

  •  75
  • Walter Bright  · 技术社区  · 16 年前

    使其成为可恢复错误的有力论据是什么?

    23 回复  |  直到 16 年前
        1
  •  37
  •   Jon Skeet    16 年前

    对于一个web服务器来说,让一个请求/响应对失败,然后继续进行进一步的请求并不是完全不合理的。但是,您必须确保单个故障不会对全局状态产生有害影响—这将是一个棘手的问题。考虑到在大多数托管环境(如.NET和Java)中,故障会导致异常,我怀疑如果在“用户代码”中处理异常,则该异常对于将来的请求是可恢复的—例如,如果一个请求尝试分配10GB内存但失败,则不应损害系统的其余部分。但是,如果系统在试图将请求传递给用户代码时内存不足,那么这种情况可能会更加糟糕。

        2
  •  17
  •   Aaron Digulla    16 年前

    但是代码可以处理任何块大小。因此,尽管使用1MB数据块可能更快,但如果您设计的系统复制了大量文件,那么明智的做法可能是找出内存错误并减小数据块大小,直到成功。

    另一个地方是存储在数据库中的对象的缓存。您希望在缓存中保留尽可能多的对象,但不希望干扰应用程序的其余部分。由于可以重新创建这些对象,所以将缓存附加到内存不足的处理程序以删除条目,直到应用程序的其余部分有足够的空间呼吸,这是一种节省内存的聪明方法。

    最后,对于图像处理,您希望将尽可能多的图像加载到内存中。同样,OOM处理程序允许您在不预先知道用户或操作系统将授予您的代码多少内存的情况下实现它。

        3
  •  9
  •   Ben Hinkle Ben Hinkle    16 年前

    MATLAB用户在进行大数组运算时,总是会出现内存不足的情况。例如,如果变量x适合内存,并且它们运行“x+1”,那么MATLAB为结果分配空间,然后填充它。如果分配失败,用户可以尝试其他方法。如果在这个用例出现时退出MATLAB,那将是一场灾难。

        4
  •  8
  •   Ifeanyi Echeruo    16 年前

    OOM应该是可恢复的,因为关闭不是从OOM恢复的唯一策略。

    对于OOM问题,在应用程序级别实际上有一个相当标准的解决方案。

    在应用程序开始或关键块开始时,预先分配该内存量。如果检测到内存不足,请释放保护内存并执行恢复。这一策略仍可能失败,但总的来说,它带来了巨大的回报。

    请注意,应用程序不需要关闭。它可以显示一个模式对话框,直到OOM条件被解决。

    我不是百分之百肯定,但我很肯定 Code Complete '(任何值得尊敬的软件工程师必读)涵盖了这一点。

    另外,您可以扩展您的应用程序框架以帮助实现此策略,但请不要在库中实施此类策略(未经应用程序同意,好的库不会做出全局决策)

        5
  •  5
  •   slim    16 年前

    我认为和很多事情一样,这是一个成本/收益分析。你呢 可以 程序试图从malloc()失败中恢复-尽管这可能很困难(处理程序最好不要因为它要处理的内存不足而出错)。

    您已经注意到,最常见的情况是清理并优雅地失败。在这种情况下,已经决定了优雅中止的成本低于恢复过程中开发成本和性能成本的总和。

    我相信你能想到你自己的例子,终止程序是一个非常昂贵的选择(生命维持机器,宇宙飞船控制,尽管第一道防线当然是要确保程序具有可预测的内存使用率,并且环境能够提供这一点。

        6
  •  5
  •   n-alexander    16 年前

    我正在开发一个为IO缓存分配内存以提高性能的系统。然后,在检测到OOM时,它会收回一部分,这样业务逻辑就可以继续进行,即使这意味着更少的IO缓存和略低的写性能。

    OOM处理的主要问题是:

    1) 能够在发生的地方重新尝试,或者能够从一个更高的点后退并重新尝试。大多数现代的程序都过于依赖语言,无法真正管理它们的结局以及如何重新尝试操作。通常情况下,如果操作的上下文不是为保留而设计的,它就会丢失

    2) 能够释放一些记忆。这意味着一种资源管理器,它知道哪些对象是关键的,哪些不是关键的,并且系统能够在发布的对象变得关键时重新请求它们

    另一个重要的问题是能够在不触发另一种情况的情况下回滚。这在高级语言中是很难控制的。

    另外,底层操作系统的行为必须是可预测的。例如,如果启用了内存过度分配,Linux就不会这样做。许多支持交换的系统将比向有问题的应用程序报告OOM更快地死去。

    而且,在这种情况下,不是您的进程造成了这种情况,因此如果有问题的进程继续泄漏,释放内存是没有帮助的。

        7
  •  4
  •   Dennis C    16 年前

    只有抓住它并正确处理它,它才是可恢复的。

    然而,在许多情况下,在多线程应用程序中,OOE也可能发生在后台线程(包括由系统/第三方库创建的线程)上。 这几乎是不可能预测的,您可能无法恢复所有线程的状态。

        8
  •  3
  •   Robert Jacques Robert Jacques    16 年前

    来自GC is的内存不足错误通常不应在当前线程内恢复(但应支持可恢复线程(用户或内核)的创建和终止)

    关于反例:我目前正在从事一个D编程语言项目,该项目使用NVIDIA的CUDA平台进行GPU计算。我没有手动管理GPU内存,而是创建了代理对象来利用D的GC。因此,当GPU返回内存不足错误时,我运行一个完全收集,并且只有在第二次失败时才会引发异常。但是,这并不是一个真正的内存不足恢复的例子,而是一个GC集成的例子。其他恢复示例(缓存、空闲列表、无自动收缩的堆栈/哈希等)都是具有自己的收集/压缩内存方法的结构,这些方法与GC分离,并且往往不是分配函数的本地方法。 因此,人们可能会实现以下功能:

    T new2(T)( lazy T old_new ) {
        T obj;
        try{
            obj = old_new;
        }catch(OutOfMemoryException oome) {
            foreach(compact; Global_List_Of_Delegates_From_Compatible_Objects)
                compact();
            obj = old_new;
        }
        return obj;
    }
    

        9
  •  1
  •   Mike G.    16 年前

    在一般情况下,它是不可恢复的。

    当然,您必须确保“转储”过程不需要新的内存分配:)此外,恢复失败的特定分配可能会很棘手,除非您能够在分配器级别直接插入缓存转储代码,这样失败就不会传播到调用方。

        10
  •  1
  •   geocar    16 年前

    这取决于你说的内存不足是什么意思。

    malloc() 在大多数系统上失败,是因为地址空间不足。

    如果大部分内存是通过缓存或mmap的区域占用的,那么您可以通过释放缓存或取消mmaping来回收部分内存。然而,这确实需要你知道你在使用内存做什么-正如你所注意到的,要么大多数程序没有,要么没有什么区别。

    setrlimit() 在您自己身上(为了防止不可预见的攻击,也许是root对您做的),您可以放宽错误处理程序中的限制。我经常这样做——如果可能的话,在提示用户并记录事件之后。

    另一方面,捕获堆栈溢出有点困难,而且不可移植。我为你写了一个矫揉造作的解决方案 ECL ,并描述了一个Windows实现。它是几个月前签入ECL的,但是如果你感兴趣的话,我可以挖掘原始补丁。

        11
  •  1
  •   Michael Borgwardt    16 年前

    特别是在垃圾收集环境中,如果您在应用程序的高级别发现OutOfMemory错误,很可能有很多内容超出了范围,可以被回收以返回内存。

    在单次超额分配的情况下,应用程序可能能够继续完美地工作。当然,如果你有一个渐进的内存泄漏,你只会再次遇到问题(更可能是早晚),但它仍然是一个好主意,让应用程序有机会去优雅地下去,保存在一个GUI应用程序的情况下未保存的更改,等等。

        12
  •  1
  •   Will Hartung    16 年前

    是的,OOM是可恢复的。作为一个极端的例子,Unix和Windows操作系统在大多数情况下都能很好地从OOM环境中恢复过来。应用程序失败了,但操作系统幸存了下来(假设首先有足够的内存供操作系统正确启动)。

    我只举这个例子来说明这是可以做到的。

    处理OOM的问题实际上取决于您的程序和环境。

    例如,在许多情况下,OOM最有可能发生的地方并不是从OOM状态中实际恢复的最佳地方。

    现在,自定义分配器可以作为代码中的中心点来处理OOM。Java分配器将在is实际抛出OOM异常之前执行完整的GC。

    您的分配器越具有“应用程序意识”,就越适合作为OOM的中心处理程序和恢复代理。再次使用Java,它的分配器并不特别了解应用程序。

    所以,是的,OOM是可恢复的,但是它可能非常困难,特别是在像Java这样的现代环境中,它有大量不同质量的第三方库。

        13
  •  1
  •   Keith Thompson    11 年前

    这个问题被标记为“语言不可知论”,但如果不考虑语言和/或底层系统,很难回答(我看见好几间卧室

    如果内存分配是隐式的,没有检测给定分配是否成功的机制,那么从内存不足状态中恢复可能很困难或不可能。

    Storage_Error

    另一方面,如果你有一个机制,试图分配内存,并能够报告失败这样做(像C的) malloc() new )是的,当然有可能从失败中恢复过来。至少在 新的

    尝试恢复是否有意义取决于应用程序。如果应用程序在分配失败后不能成功,那么它应该尽可能地进行清理并终止。但是,如果分配失败仅仅意味着一个特定的任务无法执行,或者如果任务仍然可以用更少的内存执行得更慢,那么继续操作是有意义的。

    一个具体的例子:假设我正在使用一个文本编辑器。如果我试图在编辑器中执行某个需要大量内存的操作,而该操作无法执行,我希望编辑器告诉我它不能按我的要求执行 让我继续编辑

        14
  •  0
  •   Friedrich    16 年前

    这是个难题。乍一看,似乎没有更多的记忆意味着“走运”,但你也必须看到,一个人可以摆脱许多记忆相关的东西,如果一个人真的坚持。让我们从另一个方面来看,strtok函数坏了,一方面它没有内存方面的问题。然后将Glib库中的gèu stringèu作为对应项,Glib库和基于Glib或GObject的程序中的几乎所有内容都严重依赖于内存分配。可以肯定地说,在更动态的语言中,内存分配比在更不灵活的语言(尤其是C语言)中使用得更多。但让我们看看其他选择。如果你只是在内存耗尽的情况下结束程序,即使是精心开发的代码也可能停止工作。但是如果你有一个可恢复的错误,你可以做些什么。因此,使其可恢复的论点意味着人们可以选择以不同的方式“处理”这种情况(例如,为了紧急情况而将内存块放在一边,或者降级为内存占用较少的程序)。

    所以最令人信服的原因是。如果你提供了一种恢复的方法,一个人可以尝试恢复,如果你没有选择全部取决于总是获得足够的内存。。。

    当做

        15
  •  0
  •   paercebal    16 年前

    我现在真的很困惑。

    在工作中,我们有一个捆绑的应用程序一起工作,内存不足。虽然问题是要么让应用程序包变成64位(这样,就可以在正常的Win32操作系统上超出2个go的限制),要么减少内存的使用,但是“如何从OOM中恢复”这个问题不会让我忘记。

    当然,我没有解决方案,但仍然在寻找一个C++(因为RAII和例外,主要是)。

    也许一个本应正常恢复的进程应该在原子/可回滚任务中分解其处理(即,仅使用提供强/nothrow异常保证的函数/方法),并保留一个“缓冲区/内存池”用于恢复目的。

    如果任务失败,C++ BADYOLL将释放堆栈,通过RAII释放一些堆栈/堆内存。然后,恢复功能将尽可能地修复(将任务的初始数据保存在磁盘上,以便在以后的尝试中使用),并可能注册任务数据以供以后的尝试。

    我相信使用C++强大/不可丢弃的守护者可以帮助进程在低可用内存条件下生存,即使它会类似于内存交换(即慢,有些无响应等),但当然,这只是理论。在尝试模拟这个问题之前,我只需要在这个问题上变得更聪明(即创建一个C++程序,一个自定义的新的/删除的内存有限的分配器,然后尝试在那些有压力的条件下做一些工作)。

        16
  •  0
  •   Loren Pechtel    16 年前

    失去记忆通常意味着你必须放弃你所做的一切。但是,如果您在清理时很小心,它会使程序本身保持运行状态,并且能够响应其他请求。与其说“对不起,内存不足,正在关闭”,不如让程序说“对不起,内存不足,正在关闭”

        17
  •  0
  •   sharptooth    16 年前

    内存不足可能是由于空闲内存耗尽或试图分配不合理的大数据块(如一个gig)造成的。在“耗尽”的情况下,内存不足对系统来说是全局性的,通常会影响其他应用程序和系统服务,整个系统可能变得不稳定,因此最好忘记并重新启动。在“不合理的大块头”的情况下,实际上并没有发生短缺,而且可以安全地继续下去。问题是你不能自动检测出你在哪种情况下。因此,更安全的做法是使错误不可恢复,并为遇到此错误的每种情况找到解决方法—使程序使用更少的内存,或者在某些情况下只修复调用内存分配的代码中的错误。

        18
  •  0
  •   Zuu    16 年前

    这里已经有很多好的答案了。但我想从另一个角度来做贡献。

    一般来说,任何可再利用资源的消耗都应该是可回收的。理由是程序的每一部分基本上都是一个子程序。仅仅因为一个子程序不能在这个特定的时间点完成,并不意味着程序的整个状态都是垃圾。仅仅因为停车场停满了车并不意味着你就把车弄脏了。你要么等一段时间摊位才有空,要么开车去更远的商店买饼干。

    这同样适用于磁盘空间。道理是一样的。与你暗示的堆栈溢出是不可恢复的相反,我想说的是,它是一种随意的限制。没有理由不抛出异常(弹出很多帧),然后使用另一种效率较低的方法来完成工作。

    我的两分钱:-)

        19
  •  0
  •   robert.berger    16 年前

    如果你真的失去了记忆,你就注定了,因为你再也不能释放任何东西了。

    另一个问题是碎片化。尽管您可能没有内存不足(碎片化),但您可能仍然无法分配想要的大块内存。

        20
  •  0
  •   waxwing    15 年前

    我不认为在多线程应用程序中可以实现这一点。您如何知道哪个线程是内存不足错误的真正原因?一个线程可以不断地分配新内存,并将gc根分配到99%的堆中,但第一次分配失败的情况发生在另一个线程中。

    一个实际的例子:每当我在Java应用程序(在JBoss服务器上运行)中发生OutOfMemoryError时,并不是一个线程死机,服务器的其余部分继续运行:不,有几个OOME,杀死几个线程(其中一些是JBoss的内部线程)。我不知道作为一个程序员我能做些什么来从中恢复,甚至不知道JBoss能做些什么来从中恢复。事实上,我甚至不确定你能不能做到 javadoc for VirtualMachineError 表明JVM可能在抛出这样一个错误后被“破坏”。但也许问题更多的是针对语言设计。

        21
  •  0
  •   Prof. Falken    15 年前

    uClibc有一个8字节左右的内部静态缓冲区,用于在没有更多的内存可动态分配时进行文件I/O。

        22
  •  0
  •   Community CDub    8 年前

    使其成为可恢复错误的有力论据是什么?

    使其成为可恢复的错误是因为Java允许OOM在 时间,包括可能导致程序进入不一致状态的时间。因此,不可能从OOM获得可靠的恢复;如果捕捉到OOM异常,则不能 在你的任何程序状态。看到了吗 No-throw VirtualMachineError guarantees

        23
  •  0
  •   Yoric    5 年前

    我正在研究SpiderMonkey,Firefox中使用的JavaScript虚拟机(还有gnome和其他一些)。内存不足时,您可能需要执行以下任一操作:

    1. 运行垃圾收集器。我们不会一直运行垃圾收集器,因为它会破坏性能和电池,所以当您遇到内存不足错误时,可能已经积累了一些垃圾。
    2. 释放内存。例如,去掉一些内存缓存。
    3. 取消或推迟不必要的任务。例如,从内存中卸载一些很长时间没有使用的选项卡。
    4. 日志帮助开发人员解决内存不足错误。
    5. ...

    所以是的,手动处理内存不足的错误有很多原因!

        24
  •  -1
  •   nos    15 年前

    我有这个:

    void *smalloc(size_t size) {
      void *mem = null; 
      for(;;) {
       mem = malloc(size);
       if(mem == NULL) {
        sleep(1);
       } else 
         break;
      }
      return mem;
    }