加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Linux glibc 的 mallopt 海量小内存回收问题

(2014-03-27 19:08:46)
标签:

it

分类: 工作学习
最近使用ACE的Message_Block时发现,程序运行一段时间之后内存越吃越多,即便没有请求,内存也不会下降。

在使用 valgrind 排除内存泄漏之后,把怀疑的对象转到了Message_Block上。

用ACE的测试用例改了一个测试程序:

#include "ace/Log_Msg.h"
#include "ace/Message_Block.h"
#include
#include
#include
#include


using namespace std;

#define MY_DEBUG(FMT, ...)  \
        ACE_DEBUG((LM_DEBUG, FMT, __VA_ARGS__))
void foo (void);


void create_packet(ACE_Message_Block** packet, int cnt, int packet_size)
{
  for (int i = 0; i < cnt; i++) {
    packet[i] = new ACE_Message_Block(packet_size);
  }

  cout << "Create " << cnt << "packets" << endl;
}

void delete_packet(ACE_Message_Block** packet, int cnt)
{
  for (int i = 0; i < cnt; i++) {
    delete packet[i];
  }

  cout << "Delete " << cnt << "packets" << endl;
}

void hang()
{
  while (true) {
    ACE_OS::sleep(1);
  }
}


int ACE_TMAIN (int, ACE_TCHAR *[])
{
  int cnt;
  int packet_size;

  cout<< "packet size = ";
  cin>>packet_size;
  cout<< "\n packet number = ";
  cin>>cnt;

  ACE_Message_Block **packet = new ACE_Message_Block*[cnt];
  create_packet(packet, cnt, packet_size);
  delete_packet(packet, cnt);
  malloc_trim(0);
  create_packet(packet, cnt, packet_size);
  delete_packet(packet, cnt);
  malloc_trim(0);
  create_packet(packet, cnt, packet_size);
  delete_packet(packet, cnt);
  hang();
  return 0;
}

测试时,每个block 1k,测了10万个block,结果运行到hang时,该程序的内存一直维持在最高位。

上网google了一下,发现是linux平台下 mallopt 的内存管理机制导致的。

当程序对malloc出来的内存执行 free时,它只是标识这块内存被释放了。

       void free(void *ptr) 
    {
            struct mem_control_block *free;
            free ptr sizeof(struct mem_control_block);
            free->is_available 1;
            return;
    }

至于是不是真的把这块内存返还内核就不一定了。
通过man mallopt 可以得知: 如果被free的内存太小了 (小于M_TRIM_THRESHOLD),那么glibc 出于性能的考虑会把这块内存保留在当前进程堆中,以满足该进程之后malloc的需求。

所以当进程从堆中申请了海量小内存时,就会出现该程序的内存始终都是只增不减。

解决办法:
1. 使用内存池,程序改动较大,但对程序性能和管理都是很有益
2. 显示调用 malloc_trim(0) 来强制回收被释放的堆内存。
3. 调小M_TRIM_THRESHOLD ()

这里使用的方法2,上例的程序修改如下:

#include "ace/Log_Msg.h"
#include "ace/Message_Block.h"
#include
#include
#include
#include
#include


using namespace std;

#define MY_DEBUG(FMT, ...)  \
        ACE_DEBUG((LM_DEBUG, FMT, __VA_ARGS__))
void foo (void);


void create_packet(ACE_Message_Block** packet, int cnt, int packet_size)
{
  for (int i = 0; i < cnt; i++) {
    packet[i] = new ACE_Message_Block(packet_size);
  }

  cout << "Create " << cnt << "packets" << endl;
}

void delete_packet(ACE_Message_Block** packet, int cnt)
{
  for (int i = 0; i < cnt; i++) {
    delete packet[i];
  }

  cout << "Delete " << cnt << "packets" << endl;
}
void hang()
{
  while (true) {
    ACE_OS::sleep(1);
  }
}

class worker : public ACE_Task
{
public:
  int svc()
  {
    ACE_OS::sleep(5);
    malloc_trim(0);
    return 0;
  }
}
;


int ACE_TMAIN (int, ACE_TCHAR *[])
{
  int cnt;
  int packet_size;

  cout<< "packet size = ";
  cin>>packet_size;
  cout<< "\n packet number = ";
  cin>>cnt;

/ mallopt(M_TRIM_THRESHOLD, 10240);
  ACE_Message_Block **packet = new ACE_Message_Block*[cnt];
  create_packet(packet, cnt, packet_size);
  delete_packet(packet, cnt);
  malloc_trim(0);
  create_packet(packet, cnt, packet_size);
  delete_packet(packet, cnt);
  malloc_trim(0);
  create_packet(packet, cnt, packet_size);
delete_packet(packet, cnt);
  worker *w = new worker();
  w->activate();
  hang()
;

  ACE_TRACE("main");

  ACE_DEBUG ((LM_INFO, ACE_TEXT ("%IHi Mom\n")));
  foo();
  ACE_DEBUG ((LM_INFO, ACE_TEXT ("%IGoodnight\n")));

  return 0;
}

由于alloc_trim操作过多会影响性能,所以建议在实际程序中另启一个后台线程,周期性地执行。
上面的实例也另起一个线程主要是为了验证另起的线程调用malloc_trim也能回收其他线程的空闲内存。

注1: mem_control_block 是从堆中分配的内存的描述信息
   struct mem_control_block {
    int is_available;    //这是一个标记?
    int size;            //这是实际空间的大小
    };


参考:
http://www.cnblogs.com/lookof/archive/2013/03/26/2981768.html
http://www.bccn.net/Article/kfyy/cyy/jszl/200608/4238.html
man mallopt
http://stackoverflow.com/questions/10943907/linux-allocator-does-not-release-small-chunks-of-memory


转载请注明转自高孝鑫的博客

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有