linux线程资源回收的问题(虚拟内存)
2014-03-17 14:51
239 查看
本文转自 http://luckywhu.blog.163.com/blog/static/184077944201272162239405/
程序在长时间压力测试之后发生core,检查core文件的堆栈,发现最后失败的地方是C++里面对new的调用。
部分堆栈如下
由于之前程序中,曾经发生过因为系统内存耗尽而malloc失败的事情。怀疑还是内存泄露的问题。
于是在测试过程中监测内存耗用。发现一个奇怪的现象。top时。实际物理内存(RES)消耗没有上涨,但虚拟内存(VIRT)使用却不断上升直至几百GB。最后依然是malloc失败。
首先分析程序中需要申请大量内存的地方,没有发现问题,之后分析/proc/pid/status里面的内存VmSize的上涨情况,发现上涨每次初始化的阶段上涨525MB,释放空间的时候回收423MB,也就是说每轮循环有82MB左右的内存泄露了。非常有规律。
Pmap 查看进程地址空间分配情况,发现大量10MB大小的段。也就是说每次泄露的应该是10MB左右。
无奈只能GDB跟踪程序的执行,同时观察进程的VIRT占用情况。设了几个断点之后发现每次有新线程创建时内存就有上涨情况。数了一下创建的线程数目是8个,这是client端在初始化到server的连接时连接池的大小。而每个线程差不多平均10MB的内存。在server端函数中添加日志,发现client端断掉连接之后,server端并没有相应的清理创建的这8个线程。
查看线程创建部分的代码。
创建线程时,由于是pthread_attr_t为NULL,也就是说创建的线程是Joinable的。程序中detach为true,因此所有的线程都没有被join。其执行完毕时,资源也都没有被回收,这正是VIRT内存上涨的原因。
解决方法有两个,一个是在创建线程时即指定其属性为detached的,这样线程执行完毕时会自动回收资源。另一个办法是在创建线程之后执行pthread_detach,将线程属性设置为detached。
通过添加
问题解决,内存不再上涨。
PS:
在调查该问题过程中,也发另外一个问题,在Linux下编写程序时,程序运行过程中无论是RES持续上涨还是VIRT上涨,一定是由于某种原因导致的内存泄露引起。我们一般关注较多的是RES。例如下面这段代码。
该段代码很明显有内存泄露,但TOP 出来的内存RES并没有上涨,只是VIRT上涨。出现这种现象是因为linux在分配内存时采用了一种取巧的策略,即分配时分配的是虚拟地址空间,在实际使用时才映射到物理地址。这样运行中观察到的只是VIRT上涨。此时内存已经发生泄露,虽然VIRT会上涨到实际物理内存的很多倍,但最终还是会导致问题。
程序在长时间压力测试之后发生core,检查core文件的堆栈,发现最后失败的地方是C++里面对new的调用。
部分堆栈如下
(gdb) bt #0 0x000000302af69447 in _int_malloc () from /lib64/tls/libc.so.6 #1 0x000000302af6acf0 in malloc () from /lib64/tls/libc.so.6 #2 0x000000302d3af59a in operator new(unsigned long) () from /usr/lib64/libstdc++.so.6 #3 0x000000302d3af6a9 in operator new[](unsigned long) () from /usr/lib64/libstdc++.so.6
由于之前程序中,曾经发生过因为系统内存耗尽而malloc失败的事情。怀疑还是内存泄露的问题。
于是在测试过程中监测内存耗用。发现一个奇怪的现象。top时。实际物理内存(RES)消耗没有上涨,但虚拟内存(VIRT)使用却不断上升直至几百GB。最后依然是malloc失败。
首先分析程序中需要申请大量内存的地方,没有发现问题,之后分析/proc/pid/status里面的内存VmSize的上涨情况,发现上涨每次初始化的阶段上涨525MB,释放空间的时候回收423MB,也就是说每轮循环有82MB左右的内存泄露了。非常有规律。
Pmap 查看进程地址空间分配情况,发现大量10MB大小的段。也就是说每次泄露的应该是10MB左右。
无奈只能GDB跟踪程序的执行,同时观察进程的VIRT占用情况。设了几个断点之后发现每次有新线程创建时内存就有上涨情况。数了一下创建的线程数目是8个,这是client端在初始化到server的连接时连接池的大小。而每个线程差不多平均10MB的内存。在server端函数中添加日志,发现client端断掉连接之后,server端并没有相应的清理创建的这8个线程。
查看线程创建部分的代码。
int Thread::start() { int err = pthread_create(tid_, 0, Thread::threadRunner, parm_); if(err) { return THREAD_CREATE_ERROR; } if(!detach_) { if(pthread_join(*tid_, 0)) { return THREAD_JOIN_ERROR; } return parm_->retval_; } return 0; }
创建线程时,由于是pthread_attr_t为NULL,也就是说创建的线程是Joinable的。程序中detach为true,因此所有的线程都没有被join。其执行完毕时,资源也都没有被回收,这正是VIRT内存上涨的原因。
解决方法有两个,一个是在创建线程时即指定其属性为detached的,这样线程执行完毕时会自动回收资源。另一个办法是在创建线程之后执行pthread_detach,将线程属性设置为detached。
通过添加
pthread_detach(*tid_);
问题解决,内存不再上涨。
PS:
在调查该问题过程中,也发另外一个问题,在Linux下编写程序时,程序运行过程中无论是RES持续上涨还是VIRT上涨,一定是由于某种原因导致的内存泄露引起。我们一般关注较多的是RES。例如下面这段代码。
#include <iostream> using namespace std;int main(void) { uint32_t* ip; while(1){ ip = new uint32_t[100000000]; sleep(1); } }
该段代码很明显有内存泄露,但TOP 出来的内存RES并没有上涨,只是VIRT上涨。出现这种现象是因为linux在分配内存时采用了一种取巧的策略,即分配时分配的是虚拟地址空间,在实际使用时才映射到物理地址。这样运行中观察到的只是VIRT上涨。此时内存已经发生泄露,虽然VIRT会上涨到实际物理内存的很多倍,但最终还是会导致问题。
相关文章推荐
- linux创建线程时,需注意线程内存回收问题!
- linux线程资源回收
- Linux--多线程之线程资源回收pthread_cleanup_push和pthread_cleanup_pop
- 程序退到后台的时候,所有线程被挂起,系统回收所有的socket资源问题及解决方案
- 线程退出和线程资源回收问题
- linux 线程的状态及资源回收
- Linux环境下线程的同步与互斥以及死锁问题
- 数据库连接池的配置问题-空闲线程的监控和回收. druid 1.8的一个bug
- Java线程第二弹--资源冲突问题
- 关于linux线程实时信号的一个问题
- android中的系统回收资源问题
- 【原创】构建高性能ASP.NET站点 第七章 如何解决内存的问题(前篇)—托管资源优化—垃圾回收机制深度剖析
- Activity已销毁,创建的线程未回收问题
- linux进程系列(3)父子进程变量虚拟内存地址相同但变量值不同的问题
- linux 线程操作问题undefined reference to 'pthread_create'的解决办法(cmake)
- 彻底释放Linux线程的资源
- Linux下调用fork或system启动子进程的信号和资源释放相关问题
- linux下的线程的创建退出及回收
- 构建高性能ASP.NET站点 第七章 如何解决内存的问题(前篇)―托管资源优化―垃圾回收机制深度剖析
- linux问题排查 - 高cpu占用率的进程和线程