在使用 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
轉載請註明轉自高孝鑫的博客