Linux glibc 的 ma…

最近使用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


转载请注明转自高孝鑫的博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值