服务器终止时正确处置对象


9

我正在从事大型C ++项目。它由公开REST API的服务器组成,为包含许多其他服务器的非常广泛的系统提供了一个简单且用户友好的界面。该代码库很大且很复杂,并且随着时间的流逝而没有适当的设计前期。我的任务是实现新功能并重构/修复旧代码,以使其更稳定,更可靠。

目前,服务器创建了多个长寿命对象,这些对象在进程终止时不会终止或处置。这使得Valgrind几乎无法用于泄漏检测,因为不可能区分成千上万(合法地)合法泄漏与“危险”泄漏。

我的想法是确保在终止之前将所有对象都处置掉,但是当我提出此建议时,我的同事和老板反对我指出操作系统将以任何方式释放内存(这对每个人都是显而易见的)并处置对象将减慢服务器的关闭速度(目前基本上是对的调用std::exit)。我回答说,拥有一个“干净的”关闭程序并不一定意味着必须使用它。如果我们感到不耐烦,我们可以随时致电,也可以std::quick_exit只是kill -9这个过程。

他们回答说:“大多数Linux守护程序和进程都不会在关闭时释放内存”。虽然我可以看到,但确实我们的项目确实需要准确的内存调试,因为我已经发现内存损坏,双重释放和未初始化的变量。

你怎么看?我在追求无意义的努力吗?如果没有,我该如何说服我的同事和老板?如果是这样,为什么,我应该怎么做?


除了性能参数(这是合理的!)之外,是否还要花很多精力来隔离长寿命对象并为其添加清理代码?
布朗

Answers:


7

在服务器进程中添加一个开关,该开关可在valgrind测量期间使用,这将释放所有内存。您可以使用此开关进行测试。在正常操作期间,影响将最小。

我们有一个漫长的过程,需要几分钟才能释放1000个对象。退出并让他们死亡更有效。不幸的是,正如您所指出的那样,这使得使用valgrind或任何其他工具很难检测到真正的内存泄漏。

这是对我们的测试的一个很好的折衷,同时不影响正常性能。


1
+1实用主义FTW。测量具有价值,但快速关机也具有价值。
罗斯·帕特森

2
作为命令行开关的替代方法,您可能还需要考虑在#ifdef DEBUG块内实现永久对象的删除。
Jules 2014年

3

这里的关键是这样的:

虽然我可以看到,但确实我们的项目确实需要准确的内存调试,因为我已经发现内存损坏,双重释放和未初始化的变量。

这几乎直接意味着您的代码库只是希望和字符串而已。合格的C ++程序员没有两次释放。

您绝对在进行无意义的工作,例如,您正在解决实际问题的一个小症状,即您的代码与Apollo 13服务模块一样可靠。

如果使用RAII对服务器进行正确的编程,则不会发生这些问题,并且可以消除问题中的问题。另外,您的代码有时可能会正确正确执行。因此,这显然是最佳选择。


当然,问题在于更大的前景。但是,很少有人能够找到资源和权限来重构/重写项目以更好地成形。
Cengiz Can 2014年

@CengizCan:如果要修复错误,则需要重构。这就是它的工作原理。
DeadMG 2014年

2

一种好的方法是通过分类缩小与同事的讨论范围。给定一个庞大的代码库,对于长期存在的对象,当然没有一个单一的原因,而是多个(可识别的)原因。

例子:

  • 没有任何人引用的长寿命对象(实际泄漏)。这是编程逻辑错误。修复那些优先级较低的应用程序,除非它们负责使您的内存占用量随时间增长(并降低应用程序质量)。如果它们使您的内存占用量随着时间的推移而增长,请以更高的优先级修复它们。

  • 寿命很长的对象,由于程序逻辑,它们仍然被引用但不再使用,但不会使您的内存占用增加。审查代码,并尝试找到导致该问题的其他错误。如果是有意的(性能)优化,请在代码库中添加注释。

  • 寿命长的物体“通过设计”。例如,单例模式。确实很难摆脱它们,特别是如果它是多线程应用程序。

  • 回收物。寿命长的物体不一定总是坏的。它们也可能是有益的。除了具有高频内存分配/取消分配功能之外,将当前未使用的对象添加到容器中以再次需要这种内存块时可以从中进行提取可以帮助加速应用程序并避免堆碎片。它们应该易于在关闭时释放,也许采用特殊的“工具/检查”版本。

  • “共享对象”-由多个其他对象使用(引用)的对象,没有人确切知道保存它的时间以释放它们。考虑将它们变成参考计数对象。

对那些未释放对象的真正原因进行分类后,就可以更轻松地逐案讨论并找到解决方案。


0

恕我直言,这些对象的生存期永远都不应仅仅停留在系统关闭时就死掉。众所周知,这是全局变量的优缺点。尤其是在智能指针时代,除了懒惰之外,没有其他理由这样做。但更重要的是,它给您的系统增加了一定程度的技术负担,某人某天可能不得不处理。

“技术债务”的想法是,当您采取这样的捷径时,当某人将来希望更改代码时(例如,我希望能够使客户端进入“脱机模式”或“睡眠模式” ”,或者我希望能够在不重新启动服务器的情况下切换服务器),他们将不得不下功夫去做您跳过的事情。但是他们将维护您的代码,因此他们对您的了解不如您所了解的多,因此它们将花费更长的时间(我的发言时间不会延长20%,我的发言时间会延长20倍!)。即使是您,也将需要数周或数月的时间才能使用此特定代码,并且为正确实施它而清理蜘蛛网将花费更长的时间。

在这种情况下,似乎服务器对象和“长期存在”的对象之间的耦合非常紧密……有时记录可能(并且应该)超过与服务器的连接。维护服务器中对象的每次更改可能会非常昂贵,因此通常最好将产生的对象真正地处理到服务器对象的句柄,并使用实际更改服务器的保存和更新调用。这通常称为活动记录模式。看到:

http://en.wikipedia.org/wiki/Active_record_pattern

在C ++中,我希望每个活动记录对服务器都有一个weak_ptr,如果服务器连接变暗,则可能会智能地抛出错误。这些类可以根据需要进行延迟填充或批量填充,但是这些对象的生存期仅应在使用它们的位置。

也可以看看:

在退出流程之前释放资源是否浪费时间?

另一个


This reeks of global variables您如何从“有数千个需要释放的对象”变为“它们必须是全局的”?这是逻辑上的飞跃。
Doval 2014年

0

如果您可以轻松地确定应无限期保留的对象的分配位置,则可以使用替代分配机制分配它们,这样它们就不会出现在valgrind泄漏报告中,或者看起来只是一个分配。

如果您不熟悉该想法,这里有一篇关于如何在c ++中强行定制内存分配的文章,尽管请注意,您的解决方案可能比该文章中的示例更简单,因为您根本不需要处理删除!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.