在C++的动态库中,有是为了实现Singleton等功能,经常会使用静态(static)指针变量,并在第一次使用是申请动态分配对象(new); 但其内存的释放往往依赖程序退出时,操作系统来完成内存回收。对于一般的应用,这是没有问题的,但对于C++ 的插件来说,因为其可能在服务程序中被动态的热加载/卸载(dlopen/dlclose),此时,往往会带来内存泄露问题。
下面来看个示例,来说明这种情况:这个例子中,在插件中,声明一个静态成员指针变量 _buff, 并在第1次使用是申请内存10M. 把代码编译成2个功能相同的动态库(插件),然后各 dlopen/dlclose 50次,看看程序的内存使用情况。
- DsoBase.h
#ifndef __DSO_BASE_H_ #define __DSO_BASE_H_ namespace name_1 { class DsoBase { public: DsoBase(){}; virtual ~DsoBase(){}; virtual void toString()=0; }; } #endif
- Dso.h
#ifndef __DSO_DSO_H_ #define __DSO_DSO_H_ #include <stdint.h> #include <string> #include "DsoBase.h" namespace name_1 { class Dso:public DsoBase { public: Dso() ; virtual ~Dso() ; virtual void toString(); private: static pthread_mutex_t _mutex; static char * _buff; // 静态成员变量 const uint32_t MAX_LEN; }; } #endif
- Dso.cpp
#include <stdio.h> #include "Dso.h" namespace name_1 { pthread_mutex_t Dso::_mutex = PTHREAD_MUTEX_INITIALIZER; char * Dso::_buff=NULL; // 静态成员变量 Dso::Dso():MAX_LEN(10*1024*1024) { printf("Dso constructor\n"); } Dso::~Dso() { printf("Dso destructor\n"); } void Dso::toString() { pthread_mutex_lock(&_mutex); if(_buff == NULL) {//第1次使用是申请分配10M内存 _buff=(char*)malloc(MAX_LEN); memset(_buff,0x0,MAX_LEN); printf("_buff=%p\n",_buff); } pthread_mutex_unlock(&_mutex); } } extern "C" name_1::DsoBase * CreateFun() { return new name_1::Dso(); } extern "C" void DestroyFun(name_1::DsoBase * obj) { delete obj; }
- test_main.cpp
#include <stdio.h> #include <dlfcn.h> #include "pthread.h" #include "Dso.h" typedef name_1::DsoBase * (*CreateFunT)(); typedef void (*DestroyFunT)(name_1::DsoBase * p); int main(int argc , char ** argv) { // 模拟 ha3 插件更新过程 for(int i=0; i <50; i ++ ) { //1. dl_open libdso_a.so void * dl_a=dlopen("./libdso_a.so",RTLD_NOW); if(!dl_a ) { printf("dlopen return %s", dlerror()); return -1; } CreateFunT createFun_a= (CreateFunT)dlsym(dl_a , "CreateFun"); DestroyFunT destoryFunc_a = (DestroyFunT)dlsym(dl_a , "DestroyFun"); name_1::DsoBase* obj_a=createFun_a(); obj_a->toString(); //2. dl_open libdso_b.so void * dl_b=dlopen("./libdso_b.so",RTLD_NOW); if(!dl_b ) { printf("dlopen return %s", dlerror()); return -1; } CreateFunT createFun_b= (CreateFunT)dlsym(dl_b , "CreateFun"); DestroyFunT destoryFunc_b = (DestroyFunT)dlsym(dl_b , "DestroyFun"); name_1::DsoBase* obj_b=createFun_b(); obj_b->toString(); sleep(5); //3. dl_close libdso_a.so destoryFunc_a(obj_a); dlclose(dl_a); sleep(5); //4. 使用 obj_b obj_b->toString(); //5. dl_close libdso_a.so destoryFunc_b(obj_b); dlclose(dl_b); sleep(20); } return 0; }
- Scons 的 SContruct
# -*- mode: python -*- import sys, os, os.path, platform, re, time env = Environment() env.AppendUnique(CCFLAGS = '-g') env.AppendUnique(CCFLAGS = '-m64') env.AppendUnique(CCFLAGS = '-DTARGET_64') env.AppendUnique(LINKFLAGS = '-m64') dso_sources = ['Dso.cpp' ] env.SharedLibrary('dso_a', dso_sources) env.SharedLibrary('dso_b', dso_sources) env.Program('test_main',['test_main.cpp'], LIBS=['dl'], LIBPATH='.',LINKFLAGS = '-export-dynamic' )
- 编译
[static_var_and_dynamic_lib_t2]$scons -c && scons
scons: Reading SConscript files …
scons: done reading SConscript files.
scons: Cleaning targets …
Removed Dso.os
Removed libdso_a.so
Removed libdso_b.so
Removed test_main.o
Removed test_main
scons: done cleaning targets.
scons: Reading SConscript files …
scons: done reading SConscript files.
scons: Building targets …
g++ -o Dso.os -c -g -m64 -DTARGET_64 -fPIC Dso.cpp
g++ -o libdso_a.so -m64 -shared Dso.os
g++ -o libdso_b.so -m64 -shared Dso.os
g++ -o test_main.o -c -g -m64 -DTARGET_64 test_main.cpp
g++ -o test_main -export-dynamic test_main.o -L. -ldl
scons: done building targets
- 执行测试程序
[static_var_and_dynamic_lib_t2]$./test_main
Dso constructor
_buff=0x2b65d7f31010
Dso constructor
_buff=0x2b65d8b34010
Dso destructor
Dso destructor
Dso constructor
_buff=0x2b65d9535010
Dso constructor
_buff=0x2b65da138010
Dso destructor
Dso destructor
Dso constructor
_buff=0x2b65dab39010
Dso constructor
…
- 内存使用情况—mem_user
从程序开始执行到结束, 系统 mem_user 从 1.7G (图中1M 表示内存1G )涨到近 2.7G,左右,增涨1G, 正好是2个插件(libdso_a.so 和 libdso_b.so),50次dlopen/dlclose,每次申请 10M 的内存累积量。
可见 dlclose 时,并不会释放动态库(即插件)内动态申请的内存,所以引起内存泄露。
由 udpwork.com 聚合
|
评论: 0
|
要! 要! 即刻! Now!