最近使用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
转载请注明转自高孝鑫的博客
在使用 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, ...)
void foo (void);
void create_packet(ACE_Message_Block** packet, int cnt, int packet_size)
{
}
void delete_packet(ACE_Message_Block** packet, int cnt)
{
}
void hang()
{
}
int ACE_TMAIN (int, ACE_TCHAR *[])
{
}
测试时,每个block 1k,测了10万个block,结果运行到hang时,该程序的内存一直维持在最高位。
上网google了一下,发现是linux平台下 mallopt 的内存管理机制导致的。
当程序对malloc出来的内存执行 free时,它只是标识这块内存被释放了。
至于是不是真的把这块内存返还内核就不一定了。
通过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, ...)
void foo (void);
void create_packet(ACE_Message_Block** packet, int cnt, int packet_size)
{
}
void delete_packet(ACE_Message_Block** packet, int cnt)
{
}
void hang()
{
}
class worker : public ACE_Task
{
public:
};
int ACE_TMAIN (int, ACE_TCHAR *[])
{
/ /
delete_packet(packet, cnt);
}
由于alloc_trim操作过多会影响性能,所以建议在实际程序中另启一个后台线程,周期性地执行。
上面的实例也另起一个线程主要是为了验证另起的线程调用malloc_trim也能回收其他线程的空闲内存。
注1: mem_control_block 是从堆中分配的内存的描述信息
参考:
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
转载请注明转自高孝鑫的博客