为啥程序退出时全局或静态对象会导致崩溃?

Posted

技术标签:

【中文标题】为啥程序退出时全局或静态对象会导致崩溃?【英文标题】:Why global or static object can lead to crash when program exit?为什么程序退出时全局或静态对象会导致崩溃? 【发布时间】:2014-01-16 09:07:40 【问题描述】:

在C++ Singleton design pattern、obecalp 中提到:

对于许多大型程序,尤其是那些具有动态库的程序。由于库卸载时的破坏顺序问题,任何非原始的全局或静态对象都可能在许多平台上导致程序退出时出现段错误/崩溃。这是许多编码约定(包括 Google 的约定)禁止使用重要的静态和全局对象的原因之一。

有人能详细说明为什么会发生这种情况吗?也许可以举个例子来解释一下?

【问题讨论】:

我遇到的一个例子:尝试从单例析构函数中执行 OpenGL 函数 - 在执行析构函数时,Windows 已经自动销毁了窗口以及渲染上下文并卸载了 OpenGL 库。 【参考方案1】:

您可能听说过static initialization order fiasco,其中正在构建的全局引用另一个尚未构建的全局。此问题的一般解决方案是使用惰性初始化对象(首次使用时初始化)。

好吧,如果一个对象的析构函数引用了另一个已经被销毁的对象,那么在销毁时可能会发生同样的惨败;不幸的是,这个问题没有灵丹妙药的解决方案,因为析构函数的代码可以任意复杂。

一种解决方案就是禁止使用这种不礼貌的功能。

【讨论】:

这就是为什么实际的单例对象会被动态分配,并且永远不会被删除的主要原因。 @JamesKanze:不幸的是,这也会引起问题;甚至不考虑在运行时加载/卸载库时您实际上可能希望执行析构函数(例如,因为它将内容刷新到磁盘)这一事实,一个没有正确处理它分配的单例的库只是泄漏 如果你不断地加载和卸载一个库,你通常会遇到问题。虽然在某些情况下您确实希望调用单例的析构函数(我有一个管理临时文件的析构函数,析构函数会删除它们),但它们往往是例外。大多数单例不应该被破坏。 (而且大部分库应该是静态链接的,所以不会出现加载和卸载的问题。)【参考方案2】:

我将其发布为答案,因为我不明白为什么不使用它:

只需在堆栈上(来自一个类)创建一个全局对象,然后将您想要的每个全局对象分配到那个全局对象中(成员指针,在堆上分配)。您可以拥有这些全局对象的访问器,然后在全局对象的析构函数中销毁它们,完美控制每个对象的构造/解构顺序。

哦,顺便说一句,您也可以在其中设置锁,包括在“全局”对象之间。

【讨论】:

以上是关于为啥程序退出时全局或静态对象会导致崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

为啥生成 pdb 文件会导致静态链接库的大小大幅增加?

为啥在 Objective-C 中执行 alloc 和 init 在单独的语句中会导致对象根据 Xcode 静态分析器被释放?

Android:通过静态变量传递值会导致安全问题吗?

为啥创建静态 const std::string 会导致异常?

为啥创建静态 const std::string 会导致异常?

为啥创建 VBO 时静态方法会占用内存?