在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!
