【问题标题】:operator new doesn't return 0 when running out of memory内存不足时,operator new 不返回 0
【发布时间】:2015-10-15 08:02:36
【问题描述】:

我在 Bruce Eckel 的 Thinking in C++, 2nd ed., Vol.1 第 13 章中发现了这个有趣的练习:

/*13. Modify NoMemory.cpp so that it contains an array of int
and so that it actually allocates memory instead of
throwing bad_alloc. In main( ), set up a while loop like
the one in NewHandler.cpp to run out of memory and
see what happens if your operator new does not test to
see if the memory is successfully allocated. Then add the
check to your operator new and throw bad_alloc*/

#include <iostream>
#include <cstdlib>
#include <new> // bad_alloc definition
using namespace std;

int count = 0;


class NoMemory {
  int array[100000];
public:
  void* operator new(size_t sz) throw(bad_alloc)
  {
    void* p = ::new char[sz];
    if(!p)
    {
      throw bad_alloc(); // "Out of memory"
    }
    return p;
  }
};


int main() {

  try {
    while(1) {
      count++;
      new NoMemory();
    }
  }
  catch(bad_alloc)
  {
    cout << "memory exhausted after " << count << " allocations!" << endl;
    cout << "Out of memory exception" << endl;
    exit(1);
  }
}

我的问题是:为什么这段代码在内存完全耗尽时不会抛出bad_alloc(根据 Win7 上的任务管理器的资源监视器)? 我假设全局 ::new char[sz] 永远不会返回 0,即使内存已满。但为什么?它甚至使 Win7 操作系统一旦内存不足就进入麻木、无响应的状态,仍然会继续尝试分配新的空间。

(一个有趣的补充:我也在 Ubuntu 上尝试过:bad_alloc 也没有被抛出,这个操作系统仍然没有冻结,但是这个危险的进程之前被操作系统杀死了 - 不是很聪明吗?)

【问题讨论】:

  • [OT] 这里已经说明windows的任务管理器不显示真实资源使用情况。
  • 内存不足意味着您已经分配了所有 RAM 并且没有剩余空间来扩展交换文件。这并不特定于 Windows,它适用于所有操作系统。
  • 不知道你在 Windows 中是如何做到这一点的,但是在 linux 中你可以使用ulimit 来减少特定进程的可用内存量,然后你会在之前内存不足进程被杀死。
  • 如果你有 64 位的窗口,并且超过 4GB 的内存,你也可以尝试构建一个 32 位的应用程序。

标签: c++ memory-management out-of-memory new-operator bad-alloc


【解决方案1】:

您对 operator new 的实现不正确。

void* operator new(size_t sz) throw(bad_alloc)
{
  void* p = ::new char[sz];
  if(!p)
  {
    throw bad_alloc(); // "Out of memory"
  }
  return p;
}

::new 已经抛出 std::bad_alloc 并且您不需要检查 p 指针的返回值。

如果您查看g++libstdc++ source,它们会将malloc 之后的指针与空值进行比较,因此您也应该这样做以模拟这一点:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
  void *p;

  /* malloc (0) is unpredictable; avoid it.  */
  if (sz == 0)
    sz = 1;

  while (__builtin_expect ((p = malloc (sz)) == 0, false))
    {
      new_handler handler = std::get_new_handler ();
      if (! handler)
        _GLIBCXX_THROW_OR_ABORT(bad_alloc());
      handler ();
    }

  return p;
}

所以它不会返回0,而是抛出exception。我相信你为什么不在linux上得到它的原因是在这种情况下进程总是被内核(OOM-Killer)杀死。

正如@MarcGlisse 指出的那样,您可能想要使用nothrow(noexcept)版本的new

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz, const std::nothrow_t&) GLIBCXX_USE_NOEXCEPT
{
  void *p;

  /* malloc (0) is unpredictable; avoid it.  */
  if (sz == 0)
    sz = 1;

  while (__builtin_expect ((p = malloc (sz)) == 0, false))
    {
      new_handler handler = std::get_new_handler ();
      if (! handler)
        return 0;
      __try
        {
          handler ();
        }
      __catch(const bad_alloc&)
        {
          return 0;
        }
    }

  return p;
}

如您所见,如果分配失败,它将返回 0,并且它将捕获所有可能由 new_handler 引发的异常。默认new_handler 抛出std::bad_alloc。 但即使在这种情况下,我认为OOM-Killer 会在你得到一些东西之前杀死你的应用程序。如果您的问题更多关于why is it killed?,那么我建议您阅读OOM killer 政策。

【讨论】:

    【解决方案2】:

    我发现我的错误:我调用了new 错误:new NoMemory(); 正确的做法是new NoMemory;(不带括号)

    现在它就像一个魅力,像这样:

    #include <iostream>
    #include <cstdlib>
    #include <new> // bad_alloc definition
    using namespace std;
    
    int count = 0;
    
    class NoMemory {
      int array[100000];
    public:
    
      void* operator new(size_t sz) throw(bad_alloc)
      { 
        void* p = ::new(std::nothrow) char[sz];
        if(0 != p)
          return p;
        throw bad_alloc();
      }
    };
    
    
    int main() {
      try {
        while(1) {
          count++;
          new NoMemory;
        }
      }
      catch(bad_alloc)
      {
        cout << "memory exhausted after " << count << " allocations!" << endl;
        cout << "Out of memory exception" << endl;
        exit(1);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多