c++ - 程序在新抛出 bad_alloc 之前中止

标签 c++ operating-system

下面是一个小的 C++ 程序,在“new”抛出异常之前,它显然在许多情况下被中止了:

int main(){
   try{
      while(true)
         new char[2];
   }
   catch(...){
      while(true);
   }
}

该程序首先使用MinGW/g++ 4.6.1编译,然后通过shell在32位Windows 7系统上执行。当时没有其他严重的程序(在内存/CPU 消耗方面)正在运行。程序在进入 catch block 之前终止。在 Linux(Debian 7.3、gcc/c++ 4.7.2、24GB 内存)下编译和运行程序时,程序的行为类似。 (catch block 中无限循环的原因是为了避免那里可能抛出异常的任何事情——尤其是 I/O。) 在 Windows 系统上两次启动该程序时发生了一些令人惊讶的事情(至少对我而言):如果该程序(几乎)同时在两个不同的 shell 中启动,那么两个进程都不会在抛出新异常之前终止。 同样出乎我意料的是,仅适度扩大已分配内存块的大小(通过将第四行中的“2”替换为“9”)就可以使提前终止在 Windows 系统上消失。在 Linux 机器上,需要更大幅度的扩展以避免终止:大约。防止终止需要每个 block 40,000,000 字节。

我在这里错过了什么?这是所涉及操作系统的正常/预期行为吗?如果是这样,这是否会破坏异常的有用性——至少在动态分配失败的情况下?是否可以(由用户)以某种方式修改操作系统设置以防止此类过早终止?最后,关于“严肃”的应用程序:在什么时候(w.r. 到动态内存分配)我不得不担心我的应用程序会被操作系统突然中止?

最佳答案

Is this normal/intended behavior of the involved operating systems?

是的,它被称为“过度使用”或“惰性分配”。 Linux(我认为是 Windows,但我从来没有为该操作系统编程)会在您请求时为进程分配虚拟内存,但在您访问它之前不会尝试分配物理内存。如果没有可用的 RAM 或交换空间,程序就会失败。或者,至少在 Linux 的情况下,其他进程可能会被随机杀死,因此您可以掠夺它们的内存。

请注意,当像这样进行小分配时,进程将分配较大的 block 并将它们放在堆中;因此通常会立即访问分配的内存。大分配将直接从操作系统分配,因此您的测试程序不会访问该内存 - 这就是为什么您观察到在分配大块时程序没有中止。

And if so, doesn't this undermine the usefulness of exceptions - at least in the case of dynamic allocation failure?

是的,确实如此。

Can the OS settings be modified somehow (by the user) to prevent such premature terminations?

在 Linux 上,有一个系统变量来控制过度使用策略:

echo 2 > /proc/sys/vm/overcommit_memory

值 2 意味着永不过度使用 - 如果分配请求超过当前未使用的 RAM 加上交换空间,分配将失败。 1 表示永远不会使分配失败。 0(默认)表示猜测一个分配请求是否合理。

我不知道 Windows 是否具有类似的可配置性。

关于c++ - 程序在新抛出 bad_alloc 之前中止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25366478/

相关文章:

module - Pycharm os.get_terminal_size() 不工作

c++ - 在 Windows 上默认启用 GCC 编译器 C++11 标志

operating-system - 工作,任务和流程,有什么区别

linux - 关闭 STDOUT 如何影响 printf

linux - Unix 文件系统 : How are file names translated to disk sectors?

c# - 为什么会有PlatformID.MacOSX?

c++ - 如何提高邻接表分配的速度?

c++ - 错误 C2061 : syntax error : identifier 'istream'

.net - 将非托管 C++ 与 F# 混合用于物理 : worth it?

c++ - 将一个对象发送到另一个对象并调用第一个对象方法