动态库卸载导致 shared_ptr 析构崩溃问题分析
问题现象

程序在退出时发生 SIGSEGV(段错误),崩溃位置位于 std::shared_ptr<BusinessPlugin> 的析构过程中。
根本原因
BusinessPlugin是从 动态库(. so) 中加载的类。- 在
shared_ptr对象析构 之前,该动态库已被显式调用dlclose()卸载。 - 导致
BusinessPlugin的析构函数地址失效(所处内存已被释放或 unmapped)。 - 当
shared_ptr尝试调用该析构函数时,跳转到非法内存地址 → 触发 段错误。
关键细节

- 成员变量声明顺序影响析构顺序(C++ 标准规定:成员按声明顺序构造,逆序析构)。
- 若动态库句柄(如
void* handle = dlopen(...))作为类成员,且声明在shared_ptr<BusinessPlugin>之后,则:shared_ptr先析构 → 需要调用BusinessPlugin::~BusinessPlugin();- 此时动态库可能已被
dlclose(handle)卸载(如果handle在之后才析构)→ 崩溃。
解决方案
调整成员声明顺序:
确保动态库句柄(dlopen返回的handle)在shared_ptr<BusinessPlugin>之前声明。
这样handle会在shared_ptr之后析构,保证析构BusinessPlugin时动态库仍加载。延迟卸载动态库:
手动管理生命周期,确保所有依赖该库的对象(如shared_ptr)完全析构后,再调用dlclose()。避免在动态库中定义需跨模块析构的类型:
可考虑使用 纯虚接口 + 工厂函数 模式,将对象创建和销毁限制在动态库内部。
示例(正确顺序)
cpp
class PluginManager {
void* lib_handle; // 先声明:后析构
std::shared_ptr<BusinessPlugin> plugin; // 后声明:先析构
public:
PluginManager() {
lib_handle = dlopen("libbusiness.so", RTLD_LAZY);
// ... 加载 plugin
}
~PluginManager() {
// plugin 先析构(此时 lib_handle 仍有效)
// lib_handle 后析构,随后 dlclose
}
}; ⚠️ 注意:C++ 对象析构顺序与成员声明顺序相反!