log4cxx库内存泄露解决办法
来源:互联网 发布:php大型项目框架 编辑:程序博客网 时间:2024/06/01 07:44
背景
公司的系统使用log4cxx作为日志库,近期将程序迁移到Linux环境,结果发现非常严重的内存泄露。经过分析,将内存定位到log4cxx。使用的版本为0.9.7
分析
分析log4cxx库发现,其使用引用计数控制动态内存的释放,所以在打日志的时候,会有以下的核心代码:
void Logger::forcedLog(const LevelPtr& level, const String& message, const char* file, int line){ callAppenders(new LoggingEvent(FQCN, this, level, message, file, line));}void Logger::forcedLog(const String& fqcn, const LevelPtr& level, const String& message, const char* file, int line){ callAppenders(new LoggingEvent(fqcn, this, level, message, file, line));}
上述代码中,new出来的对象指针存放在一个指针对象中,在callAppenders调用结束之后,该指针对象将会析构。在其析构过程中,会通过引用计数去释放new出来的LoggingEvent对象,从而达到内存释放的目的。
现在问题出来了,new出来的对象实际上并未被释放,log4cxx的引用计数机制在linux下并没有生效。引用计数核心代码在objectimpl.cpp中,如下
void ObjectImpl::addRef() const{ Thread::InterlockedIncrement(&ref);}void ObjectImpl::releaseRef() const{ if (Thread::InterlockedDecrement(&ref) == 0) { delete this; } }
我们在看看InterlockedIncrement 和 InterlockedDecrement的具体实现源码,在thread.cpp中,如下
long Thread::InterlockedIncrement(volatile long * val){ #ifdef __GLIBCPP__ return __exchange_and_add((volatile _Atomic_word *)val, 1 ) + 1;#elif defined(__i386__) long ret; __asm__ __volatile__ ("lock; xaddl %0, %1" : "=r" (ret), "=m" (*val) : "0" (1), "m" (*val)); return ret+1;#elif defined(sparc) && defined(__SUNPRO_CC) sparc_atomic_add_32(val, 1); return *val;#elif defined(HAVE_MS_THREAD)#if _MSC_VER == 1200 // MSDEV 6 return ::InterlockedIncrement((long *)val);#else return ::InterlockedIncrement(val);#endif // _MSC_VER return *val + 1 // unsafe#endif}long Thread::InterlockedDecrement(volatile long * val){#ifdef __GLIBCPP__ return __exchange_and_add((volatile _Atomic_word *)val, -1 ) - 1;#elif defined(__i386__) long ret; __asm__ __volatile__ ("lock; xaddl %0, %1" : "=r" (ret), "=m" (*val) : "0" (-1), "m" (*val)); return ret-1;#elif defined(sparc) && defined(__SUNPRO_CC) sparc_atomic_add_32(val, -1); return *val;#elif defined(HAVE_MS_THREAD)#if _MSC_VER == 1200 // MSDEV 6 return ::InterlockedDecrement((long *)val);#else return ::InterlockedDecrement(val);#endif // _MSC_VER return *val - 1; // unsafe#endif}
这段代码非常让人纠结,根本看不懂啊,这么多环境相关的宏,谁知道走的是哪段代码啊?没办法,只好写一个程序测试自己的Linux系统都命中了哪些宏,测试的结果让人大跌眼镜。接口中的宏没有一个命中!!坑爹啊,这是。结果在Linux下,这2个接口都成为了空函数,啥也没干,连return语句都没有。注意,接口中//unsafe那一行其实也没有编译到代码中!
我以为//unsafe对应的代码段应该是所有宏都未定义的时候所编译的代码段,于是加上#else让其能够得到编译,结果。。。程序运行出core;好吧,是因为没有完成实际的计数值增减操作,那就加上代码完成增减,为此我使用了各种能想到的linux原子级加锁增减接口,结果还是不行,仍然出core。最后都快崩溃了。
解决
经过各种尝试不行之后,突然想到,是否可以抛弃log4cxx中的引用计数机制呢?于是回到前面的Logger::forcedLog接口。不就是内存释放,不用引用计数,手动释放还不行吗?于是我修改了这段代码,如下
void Logger::forcedLog(const LevelPtr& level, const String& message, const char* file, int line){ LoggingEvent* pLog = new LoggingEvent(FQCN, this, level, message, file, line); callAppenders(pLog); delete pLog;}void Logger::forcedLog(const String& fqcn, const LevelPtr& level, const String& message, const char* file, int line){ LoggingEvent* pLog = new LoggingEvent(fqcn, this, level, message, file, line); callAppenders(pLog); delete pLog;}
这样,每一次打日志之后都手动释放内存。然后就是编译、测试,多线程运行,结果内存非常稳定,done!
总结
开源库不是万能的,条件编译也不是万能的,总会出现这样那样不同的环境导致库出现一些问题。好在可以看到源码。所以还是有办法解决的。当然,作者的解决方案其实并不是特别完美的,也有可能会出现其他的问题,希望各位大神不吝指教~
更多技术博客,请关注:www.chenkeblog.com
- log4cxx库内存泄露解决办法
- Skia库内存泄漏
- com库内存管理
- OpenGL扩库内存泄漏
- boost库内存池使用
- 动态链接库内存分配(转)
- 标准模板库内存释放问题
- libdecodeqr二维码识别库内存泄漏检测
- log4cxx 在VS2008下报内存泄露
- java之内存泄露
- Android之内存泄露
- Android之内存泄露
- AsyncTask之内存泄露
- Log4cxx
- Log4cxx
- LOG4CXX
- log4cxx
- log4cxx
- POJ1008:Maya Calendar
- Search in Rotated Sorted Array
- 最小公倍数
- 安卓开发中调用WebService实例
- 部署在wcf rest服务上的wcf rest服务调用页面程序
- log4cxx库内存泄露解决办法
- C#中加密和解密
- hdu 1588 (Fibonacci+二分+矩阵快速幂)
- CreateEvent的用法
- IIS 7.5 配置伪静态详细图文
- 在2013年最新百度算法更新频繁的情况下,如何做SEO优化,让SEO突围而出?
- hdu 2181 DFS
- 一层循环输入99乘法口诀表
- cvSetCaptureProperty