动态库卸载时单例对象不会自动安全销毁——析构函数大概率不执行,导致资源泄漏、悬垂指针或段错误;必须由宿主显式调用跨平台 shutdown 接口(如 cleanupsingleton)并严格控制调用顺序。

static Singleton& getInstance() { static Singleton instance; } 在 .so 或 .dll 里,instance 的析构函数大概率不会执行,导致资源泄漏、悬垂指针、段错误。
Linux 下 __attribute__((destructor)) 为什么常失效
它确实会在 dlclose 后触发,但有硬限制:
– 执行时机无序,不能依赖其他静态对象(比如 std::cout、std::mutex 已不可用)
– 若单例内部调用了日志库或配置模块的符号,而那些库已先卸载,SEGFAULT 立刻发生
– 编译时若启用了 -fvisibility=hidden,该函数可能被剥离,根本不会注册
– 它无法区分是 dlclose 还是进程退出触发,后者中堆管理器可能已失效,delete 会崩
Windows 下 DllMain 的 DLL_PROCESS_DETACH 不可靠在哪
DLL_PROCESS_DETACH 看似理想,但实际陷阱密集:
– 必须检查 lpReserved == nullptr 才能确认是 FreeLibrary 触发;若为非空,说明是进程退出,此时 CRT 堆可能已关闭,delete 会触发 Access Violation
– DllMain 中禁止调用任何可能加载新 DLL 的 API(如 LoadLibrary、printf),连 std::thread::join() 都可能隐式触发 DLL 加载
– 若单例持有 std::thread,在 DLL_PROCESS_DETACH 里调用 join() 极易死锁(线程函数内又调用了本 DLL 的其他函数)
必须暴露显式的 shutdown() 接口并由宿主控制调用
这是唯一跨平台、可测试、可调试的方案:
– 在动态库中导出 C 风格函数:extern "C" __declspec(dllexport) void CleanupSingleton();(Windows)或 extern "C" void cleanup_singleton();(Linux)
– 实现中只做三件事:检查原子标志(std::atomic<bool> s_destroyed{false}</bool>)、调用 delete m_instance、置 m_instance = nullptr
– 禁止在 CleanupSingleton 中调用任何外部模块函数(包括日志、网络、文件操作),所有清理逻辑必须收束到本库内
– 宿主程序调用顺序必须严格为:CleanupSingleton() → FreeLibrary() 或 dlclose();中间不能跳过,也不能有异常穿透
– 若单例内部用了 thread_local 资源(如 per-thread socket),需在 CleanupSingleton 中先设停用标志,再等待线程自然退出或超时强制终止
CleanupSingleton 执行成功,只要宿主或其他插件仍持有旧指针,后续任意一次 getInstance() 返回的都是 nullptr 或已释放内存——这种崩溃往往延后数秒甚至数分钟才爆发,调试时几乎无法关联到卸载点。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











