当 运行 内存不足时,operator new 不会 return 0

operator new doesn't return 0 when running out of memory

我在 Bruce Eckel 的 Thinking in C++,第 2 版,第 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);
  }
}

我的问题是:当 运行 完全内存不足时(根据 Win7 上任务管理器的资源监视器),为什么这段代码不抛出 bad_alloc? 我假设全局 ::new char[sz] 永远不会 returns 为 0,即使内存已满。但为什么?一旦 运行 内存不足,它甚至会将 Win7 OS 变成麻木的无响应状态,但仍然会继续尝试分配新的 space.

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

您对 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 指针的 return 值。

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

_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;
}

所以它不会 return 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;
}

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

我发现了我的错误:我调用了 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);
  }
}