为了不减慢关机速度,首选内存泄漏

Memory leaks preferring in order to not to slow down shutdown

本文关键字:内存 泄漏 速度 关机      更新时间:2023-10-16

Chromium的文档说:

注意:Singleton和base::LazyInstance都提供了"leaky"特性来在关闭时泄漏全局。这通常是可取的(除非在库代码中,代码可能被动态加载到另一个进程的地址空间中,或者在进程关闭时需要刷新数据),以便不减慢关闭速度。

在这种情况下这是可以接受的?

"可接受"有点难以定义,因为我个人倾向于避免这种做法。例如,我们使用泄漏检测工具,该工具最终会显示假阳性,除非另有抑制(尽管我注意到在Chromium的文档中,它们的泄漏特性抑制了它:我不这样做,并将假阳性警告视为需要修复而不是抑制的东西)。

当然,只有当目标操作系统在关闭进程时回收单例模式获得的资源时,这才可以接受。大多数现代操作系统对最常见的资源类型都这样做。当然,如果你的单例创建了一个巨大的临时文件,明智的做法是通过显式的关闭过程来销毁它。

关闭速度

简单地避免手动回收资源以快速关闭的理由是合理的,但它通常伴随着一种心态,即在最小的块中分配大量内存。

在使用链接结构或针对通用分配器单独分配的大量小抽象对象时,通常会出现这种情况。如果有数百万个小内存块需要单独释放,这可能会导致痛苦的停机时间长达几秒钟。

然而,启动、加载输入数据和关闭的速度往往是相互关联的。如果我们想要加快所有这些操作的速度,那么批量预分配和池化内存会很有帮助。

使用这种批量资源分配和池化策略,应用程序可以启动和关闭,并且总体上运行得很快。这可能很难应用,但如果你有这种情况,应用程序通常能够安装关闭,即使加载了千兆字节的数据,并且可能只是泄漏资源将开始看起来比优化技术更懒惰,并且可能将其推向"不可接受"的美学。YMMV .

然而,在实际需要性能的情况下,您可以做您需要做的事情。我猜想,在为泄漏检测器设计泄漏特性和抑制器之前,Chromium开发人员的代码库结构是有益的。每个人看到的场景都不一样。

关闭正确性

在一些非常复杂的代码库中,有很多插件在关机时加载和卸载,例如,让该死的应用程序正确关闭实际上是非常困难的。

我曾经查看过一个代码库,在那里我们以这种方式把自己逼到一个角落,实际上我们的大部分bug都与应用程序在关闭时崩溃有关(最不需要用户临界时间崩溃,但仍然非常烦人)。

我们的问题是加载插件时创建的单例和其他类型的全局,在创建时触发外部状态的副作用(其他全局或其他单例生活在不同的模块中)。在这些情况下,在销毁这样一个全局变量(例如:从一个全局集合中删除添加到一个全局集合中的全局变量)时,会出现逆转这些副作用的对称愿望。这就引入了销毁顺序的问题(由于许多作者编写的分散代码和分散在许多插件中的全球状态,这在这里非常困难)。

当全局变量在销毁过程中开始依赖其他全局变量(惰性构造或非惰性构造)时,仅仅确保以适当的顺序销毁东西,以便全局变量在关闭过程中不会最终依赖另一个已经失效的全局变量(甚至可能已经卸载了dylib),事情就会变得相当尴尬。

我们几乎肯定在那里做了各种各样的错误(从使用太多的单例和全局开始,并以一种特殊的方式将它们分散到插件中,没有统一的设计心态),这种经历使我不喜欢在这些复杂的场景中使用各种单例和全局(不是因为灵活性降低,而是因为编写正确的关闭代码的难度)。但是,如果我们不麻烦地试图让这些全局变量在销毁时逆转其他全局变量的副作用,简单地把它们留在那里,让操作系统清理资源,可能会容易得多。在那里,我可以理解只是泄漏而不破坏东西的诱惑,或者即使我们破坏了东西,也不会逆转对其他全局的副作用。这是对一个非常有缺陷的设计的一个丑陋的变通方法,但我能理解这种问题比通过泄漏来解决性能问题要容易一些。

在任何情况下,什么是"可接受的"会有很大的不同。它的范围将从美学一直延伸到可移植性。这是我第一次遇到简单地泄漏单例的效率争论,但是我可以理解什么样的场景可能导致这种情况。

当所有内存(和其他资源)被回收并在关机时释放回操作系统时。这是在linux系统上发生的情况。