了解新处理程序的行为

Understand the behavior of the new-handler

本文关键字:程序 处理 新处理 了解      更新时间:2023-10-16

我正在阅读Scott Meyers的《有效的C++55》,有一个来自第49项的问题:

operator new无法满足内存请求时,它调用新的处理程序函数重复执行,直到找到足够的内存为止。

一个设计良好的newhandler函数必须执行以下操作之一:

  • 释放更多可用内存
  • 安装不同的新处理程序
  • 卸载新处理程序
  • 引发异常
  • 不返回

new无法分配内存时,意味着内存不足,问题是newhandler如何以及从哪里分配更多内存?

你能解释一下所有这些步骤吗?

这取决于实现。我可以告诉你我通常的做法:

1) 新的处理程序在启动时分配大量内存作为保留。

2) 当普通分配失败时,新的处理程序会动用其储备。

3) 控制负载管理的代码可以挂接内存管理系统,并确定它何时已进入其保留区。它通常通过修剪缓存和减少负载来做出反应。

4) 内存管理器试图在释放内存时重新填充其保留空间。

5) 当保留被恢复时,钩子会被通知它们可能会增长缓存和/或恢复接受额外的负载。

6) 当储备变低时,可能失败的分配(通常是大额分配)就会失败。所有代码都必须理智地处理大型分配的失败。

7) 如果储备耗尽,无法失败的分配(通常是小的分配)就会阻塞。

8) 如果阻塞情况持续存在,或者大量分配继续失败,并且保留无法恢复,则会触发异常终止。

它可以丢弃并非真正需要的数据。比如说,photoshop以几个比例缓存显示图像,并尽可能多地保存。这就是它知道自己能逃脱多少惩罚的方式。

不返回

它只是尽其所能,但未能提供更多可用内存。它调用exit()abort()并终止进程。

这可以用作快速失效策略。如果系统内存不足,并且已知不需要某些进程(例如:子进程),则调用abort()将为其他进程释放内存,这是摆脱进程的最快方法。

引发异常

抛出bad_alloc意味着分配器无法释放任何内存,并且应用程序是否可以通过某种方式恢复。我个人不在乎实际捕获bad_alloc,因为它不应该发生,或者我对此无能为力。

如果您使用的是在内存不足时返回nullptr以强制它们抛出的古代编译器,则可以使用此方法。不过,这是否可行还值得怀疑。

卸载新处理程序

查看调用set_new_handler(nullptr);的书应该会卸载新的处理程序。根据Scott Meyer的说法,这应该会导致operator new抛出bad_alloc异常。这应该与您自己抛出异常相同。

安装不同的新处理程序

由于new_handler被调用,直到分配错误得到解决(即:operator new没有解决),如果new_handler提供的实现不能解决问题,则它应该将条件传播到另一层。

如果我自己安装new_handler,我会保留对以前的new_handler的引用(另请参见:get_new_handler()),以便在我的算法失败时将其交换回来。

释放更多可用内存

这里的假设是应用程序本身安装了new_handlernew_handler的作者在这里有机会反思或修改分配的内存。所以程序员安装了new_handler,因为他知道有一些内存可以释放。如果一切都出了问题,有一个策略可以从这次失败中恢复过来。

可能发生的事情的不完整列表(部分猜测):

  • 您的操作系统允许您手动调用内存不足杀手(以释放内存)
  • 如果您正在使用Boehm垃圾收集器,那么实际上可以调用GC_gcollect();来强制进行垃圾收集
  • 你自己保留了一些内存,现在就开始使用
  • 您可以手动调用delete来删除已知的可开销的不重要对象(缓存)
  • 你有一个像Perl、Python或Mono一样运行的解释器,并调用它的垃圾收集器来告诉他们你的内存不足(或者他们自己在init()例程上安装新的处理程序)

实施

以下是libstdc++的实现。