Meyer单例在动态库中必然失效:dlclose/FreeLibrary大概率跳过析构,导致悬垂指针、资源泄漏和SEGFAULT;必须改用堆分配+std::call_once初始化+显式shutdown销毁,由宿主程序严格控制生命周期。
动态库卸载时,
这种 Meyer 单例**一定不可靠**——不是“可能崩溃”,而是
和
下大概率跳过析构,留下悬垂指针、未关闭句柄、未 join 线程,后续任意访问即段错误。
为什么 Meyer 单例在 .so/.dll 里必然失效
局部静态变量的析构由运行时统一调度,但动态库卸载不触发该调度:glibc 在多数
场景下直接跳过静态局部变量析构;Windows 的 CRT 也不保证
中执行 C++ 静态对象析构。更危险的是:
析构函数若调用
、日志宏、
,这些符号可能已从地址空间卸载,直接
所在堆内存没被回收,但析构函数没跑 →
仍在运行、
没
、内存池没释放
其他模块持有该单例裸指针(如插件传入的
),卸载后解引用就是未定义行为
必须改用堆分配 +
+ 显式
放弃“自动析构”幻想,把生命周期控制权拿回来。核心是三件事:加载时强制构造、运行时安全访问、卸载前主动销毁。
声明为
,而非局部静态对象或全局静态对象
内用
保证线程安全初始化
提供导出函数
(Linux)或
(Windows)
内部先检查
,幂等;然后
,再置
中禁止调用任何外部 API:不碰
、不调日志、不访问其他单例、不 new/delete、不抛异常
Windows 必须在
的
中判空
这是 Windows 平台最可控的卸载钩子,但关键在区分卸载来源:进程退出时不能清理,否则会访问已销毁的 CRT 或堆。
C知道
CSDN推出的一款AI技术问答工具
下载
立即学习
“
C++免费学习笔记(深入)
”;
仅当
时,才表示本次卸载由
主动触发,此时可调用
必须和
逻辑一致:原子判空、加锁(如有并发访问)、
、置空指针
禁止在
中调用
、
等可能引发 DLL 加载/卸载依赖的函数;资源清理必须提前在
中完成
返回前应检查
,否则抛
Linux 的
只能作兜底,不能当主力
它会在
后执行,但执行环境极度受限:STL、malloc、甚至 libc 符号都可能已不可用。它不是清理入口,而是“提醒你漏了事”的哨兵。
定义
—— 不尝试
,只做致命提醒
若真要执行清理,必须确保
内部只操作原始资源:
、
、
编译时禁用
,否则
函数可能被剥离
真正可靠的清理路径永远是宿主程序显式调用
,再
最易被忽略的一点:所有跨模块依赖(比如单例内部调用了
)必须在
中**提前断开**,不能等到析构函数里去访问——因为那个时刻,
所在的库可能已经卸载了。
static Singleton& getInstance() { static Singleton instance; }dlcloseFreeLibrarydlcloseDLL_PROCESS_DETACHstd::coutConfig::getInstance()SEGFAULTinstancestd::threadint fdclose()Logger*std::call_onceshutdown()static Singleton* instance = nullptr;getInstance()std::call_once(flag, []{ instance = new Singleton(); });extern "C" __attribute__((visibility("default"))) void Config_shutdown();extern "C" __declspec(dllexport) void Config_shutdown();shutdown()std::atomic s_destroyed delete instance;instance = nullptr;shutdown()std::coutDllMainDLL_PROCESS_DETACHlpReservedlpReserved == nullptrFreeLibrarySingleton::destroyInstance()destroyInstance()shutdown()deleteDllMainstd::mutex::lock()std::thread::join()destroyInstance()getInstance()instance != nullptrstd::runtime_error("Singleton destroyed")__attribute__((destructor))dlclosestatic void __attribute__((destructor)) so_cleanup() { if (instance != nullptr) abort(); }deletedestroyInstance()if (fd != -1) close(fd);if (handle) CloseHandle(handle);if (ptr) free(ptr);-fvisibility=hiddendestructordlsym(handle, "Config_shutdown")dlclose(handle)NetworkManager::getInstance()shutdown()NetworkManager