您的位置:首页 > 运维架构 > Linux

linux线程资源回收的问题(虚拟内存)

2014-03-17 14:51 239 查看
本文转自 http://luckywhu.blog.163.com/blog/static/184077944201272162239405/
程序在长时间压力测试之后发生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会上涨到实际物理内存的很多倍,但最终还是会导致问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: