new
操作符在实际应用中会抛出异常吗?
如果是,除了终止应用程序之外,我还有其他处理这种异常的选择吗?
更新:
在现实世界中,负责创建对象的 new
操作是否会检查失败并进行恢复,以避免内存不足的情况发生?
new
操作符在实际应用中会抛出异常吗?
如果是,除了终止应用程序之外,我还有其他处理这种异常的选择吗?
更新:
在现实世界中,负责创建对象的 new
操作是否会检查失败并进行恢复,以避免内存不足的情况发生?
是的,new
操作符可能会抛出异常,如果内存不足或者尝试分配过大的内存块都可能导致这种情况。
你可以捕获std::bad_alloc
异常并根据需要进行处理。有时候这样做是有意义的,但大多数情况下(也就是说,通常)并不需要处理它。例如,如果你尝试分配一个巨大的缓冲区,但实际上只需要较小的空间,那么你可以尝试逐步分配更小的块来解决问题。
std::bad_alloc
异常。在大多数情况下,您无法做太多事情,因此最好的选择可能是在main
函数中捕获它,并且至少要向用户提供友好的错误消息或记录失败(包括Herb Sutter在内的许多专家都同意这一点:http://www.gotw.ca/publications/mill16.htm)。 - James McNellismalloc
成功或死亡)函数称为xmalloc
。 - C. K. Youngstd::set_new_handler
在 <new>
中是标准的 C++,§18.4.2.2-3。如果您有某种垃圾回收机制,或者想记录错误,使用它是完全合理的。通过 throw bad_alloc
退出 new_handler 不是一个坏主意。 - Potatoswatter如果您正在运行一个没有虚拟内存的典型嵌入式Linux处理器上,如果您分配了太多内存,那么在新建失败之前,操作系统很可能会终止您的进程。
如果您的程序在物理内存少于虚拟内存最大值(标准Windows为2 GB)的机器上运行,则会发现一旦您分配的内存量大约等于可用的物理内存,进一步的分配将成功,但会导致分页到磁盘。这会拖慢您的程序,您实际上可能无法达到耗尽虚拟内存的点。因此,您可能不会抛出异常。
如果您有更多的物理内存而不是虚拟内存,并且您只需保持分配内存,那么当您耗尽虚拟内存以至于无法分配所请求的块大小时,您将收到一个异常。
如果您有一个长时间运行的程序,包括小块和寿命各异的各种不同块大小的分配和释放,那么虚拟内存可能变得碎片化,以至于新建将无法找到足够大的块来满足请求。然后新建将抛出异常。如果您偶尔泄漏随机位置的小块,则最终会将内存片段化到足以使任意小的块分配失败,并抛出异常。
如果您有一个程序错误,意外地向new[]传递了一个巨大的数组大小,则new将失败并抛出异常。例如,如果数组大小实际上是某种随机字节模式,可能源于未初始化的内存或损坏的通信流。
所有以上内容都是针对默认全局new的。但是,您可以替换全局new并提供类特定的new。它们也可以抛出异常,该情况的含义取决于您如何编程。通常,new包括一个循环,尝试获取请求的所有可能途径。当所有这些耗尽时,它会抛出异常。然后您要做什么取决于您。
您可以捕获new操作符的异常,并利用此机会记录异常发生时程序状态。您可以“dump core”(将内存转储到磁盘)。如果在程序启动时分配了循环仪表缓冲区,则可以在终止程序之前将其转储到磁盘。相比不处理异常,程序终止可以是温和的,这是一个优点。
我个人没有见过异常后可以获得额外内存的情况。然而,有一种可能性:假设您有一个内存分配器,它非常高效,但不能很好地回收已释放空间。例如,它可能容易出现空闲空间碎片化,即相邻但未合并的空闲块。您可以使用new操作符引发的异常,在new_handler中捕获,然后运行一种自由空间压缩过程,然后重试。
严肃的程序应将内存视为潜在的稀缺资源,尽可能控制其分配,监视其可用性,并在出现问题时采取适当措施。例如,您可以认为在任何真正的程序中,传递给内存分配器的大小参数有一个相当小的上限,而任何大于此上限的请求都应导致某种错误处理,无论是否可以满足请求。您可以主张应监视长期运行程序的内存增长率,并且如果可以合理预测程序将在不久的将来耗尽可用内存,则应开始有序重启进程。
ulimit
)运行长时间运行的进程,以便不会消耗所有系统的内存。如果您的程序达到了该限制,您将收到std::bad_alloc
。
operator new
(例如,某些版本的Visual Studio默认情况下不会抛出异常,而是返回一个类似于malloc
的NULL
指针。)catch
std::bad_alloc
异常,或者明确使用nothrow new
来返回NULL
而不是抛出异常。(还请参阅过去的StackOverflow帖子有关此主题的讨论。)malloc
类似,operator new
在内存耗尽、地址空间耗尽(例如,在32位进程中为2-3GB,具体取决于操作系统)、配额耗尽(ulimit
已经提到)或连续地址空间耗尽(例如,堆被分片)时会失败。你不需要在每个new
中处理异常 :) 异常可以传播。设计代码时,应该在每个“模块”中的某些点处理错误。
是的,new
可以抛出 std::bad_alloc
异常(std::exception
的子类),你可以捕获它。
如果你绝对想避免这个异常,并且准备测试 new
的结果是否为 null 指针,你可以添加一个 nothrow
参数:
T* p = new (nothrow) T(...);
if (p == 0)
{
// Do something about the bad allocation!
}
else
{
// Here you may use p.
}
main()
的末尾(如果您是多线程,则在每个线程方法的末尾)保留一个catch
,并在退出之前显示一个大型错误消息“内存不足”。 :) - vladr是的,如果没有更多可用内存,new
将抛出异常,但这并不意味着您应该在每个 new
前加上 try ... catch
代码块。只有在程序可以真正处理异常情况时才要捕获异常。
如果程序无法处理异常情况(通常情况下,当内存耗尽时),那么捕获异常就没有意义了。如果唯一可以合理做的事情是中止程序,那么您也可以让异常沿袭到最高级别,在那里它将终止程序。
char *pCrashMyMachine = new char[TWO_GIGABYTES];
敢试就来试试吧!
new
...正如其他人所提到的,除非你有处理它的策略,否则你不应该捕获异常。 - Potatoswattera.allocate()
抛出std::bad_alloc
是相当常见的。虽然它不是new
运算符,但仍然...... - Mooing Duck