前言
有个写好的cm有内存泄漏,跑了一天,给客户的计算机内存(好像还蛮大的内存)用的差不多了。
当停掉这个cm时,内存一下就正常了(不知道是不是OS给回收了)。
就在找这个内存泄漏问题,bug还没找完。
先发现了一个由map.insert使用不当引起的内存泄漏。
按照c++ reference的说法,同一个key,不同的value, 插入map时。map会更新这个value.
我做的实验,不会更新value.
正确的做法。插入key-value之前,需要find一下key.
如果key不在,选择插入。
如果key存在,可以按照业务逻辑,不插入或更新value.
如果没有将value插入map, 就需要删掉要插入的value(假设value是指针)。
写C程序时,还是要细致点。有些不常用的知识点,如果不太确定,可以先做个实验,再写正式代码。
写好后,运行一些可以想到的测试用例,保证加入的功能模块是正确的(逻辑正确,无内存泄漏,最好性能也好点)。
这样做以后,回头来改bug的概率就小了。等代码堆多了,再回头来修bug, 找bug也要一些时间的。
实验
// @file main.cpp
// view date time
// date "+DATE: %m/%d/%y%nTIME: %H:%M:%S"
// set date time
// date -s "2018-3-13 14:25:00"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <map>
#include <assert.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
// 不管是否delete, 最后指针都要赋值为空
#define SAFE_DELETE(p) \
do { \
if (NULL != (p)) { \
delete (p);\
} \
(p)=NULL; \
} while (0);
class class_my_Key {
public:
uint ip;
ushort port;
class_my_Key() {
}
class_my_Key(uint ip, ushort port) {
this->init(ip, port);
}
void init(uint ip, ushort port) {
this->ip = ip;
this->port = port;
}
bool operator <(const class_my_Key &r) const {
if (this->ip < r.ip) {
return true;
} else if (this->ip > r.ip) {
return false;
}
//ip == ip
if (this->port < r.port) {
return true;
} else if (this->port > r.port) {
return false;
}
//port == port
return false;
}
};
class class_my_session {
public:
uint ip;
ushort port;
std::string str_name;
public:
class_my_session() {
this->ip = 0;
this->port = 0;
str_name="unknown";
}
virtual ~class_my_session() {
}
void copy(class_my_session* psrc) {
this->ip = psrc->ip;
this->port = psrc->port;
this->str_name = psrc->str_name.c_str();
}
void show_info() {
printf("name = [%s] : ip = [%d] : port = [%d]\n",
str_name.c_str(),
ip,
port);
}
};
std::map<class_my_Key, class_my_session *> g_ses_map;
typedef std::map<class_my_Key, class_my_session *>::iterator Ses_Map_It;
typedef std::pair<class_my_Key, class_my_session *> Ses_Map_Pair;
typedef std::pair<Ses_Map_It, bool> Ses_Map_Pair_rc;
void init();
void uninit();
class_my_session* gen_ses1();
class_my_session* gen_ses2();
class_my_session* gen_ses3();
void insert_to_map_err(class_my_session*& ses);
void insert_to_map_ok(class_my_session*& ses);
void show_map();
void free_map();
void fn_map_usage_err();
void fn_map_usage_ok();
int fn_test();
int main(int argc, char** argv)
{
init();
fn_test();
uninit();
return EXIT_SUCCESS;
}
int fn_test()
{
fn_map_usage_err();
fn_map_usage_ok();
}
void init()
{
}
void uninit()
{
}
class_my_session* gen_ses1()
{
class_my_session* ses1 = new class_my_session();
ses1->ip = 11111;
ses1->port = 111;
ses1->str_name = "ses1";
return ses1;
}
class_my_session* gen_ses2()
{
class_my_session* ses1 = new class_my_session();
ses1->ip = 11111;
ses1->port = 111;
ses1->str_name = "ses2";
return ses1;
}
class_my_session* gen_ses3()
{
class_my_session* ses1 = new class_my_session();
ses1->ip = 11111;
ses1->port = 111;
ses1->str_name = "ses3";
return ses1;
}
void show_map() {
Ses_Map_It it = g_ses_map.end();
for (it = g_ses_map.begin(); it != g_ses_map.end(); it++) {
if (NULL != it->second) {
it->second->show_info();
}
}
}
void insert_to_map_err(class_my_session*& ses)
{
Ses_Map_It it = g_ses_map.end();
Ses_Map_Pair_rc rc_pair;
if (NULL != ses) {
class_my_Key key(ses->ip, ses->port);
it = g_ses_map.find(key);
if (it != g_ses_map.end()) {
printf("find key\n");
} else {
printf("not find key\n");
}
rc_pair = g_ses_map.insert(Ses_Map_Pair(key, ses));
printf("insert %s, status = %s, ses_map.size() = %d\n",
(rc_pair.first != g_ses_map.end()) ? "true" : "false",
rc_pair.second ? "was insert" : "was update",
(int)g_ses_map.size());
show_map();
printf("\n");
}
}
void insert_to_map_ok(class_my_session*& ses)
{
Ses_Map_It it = g_ses_map.end();
Ses_Map_Pair_rc rc_pair;
if (NULL != ses) {
class_my_Key key(ses->ip, ses->port);
it = g_ses_map.find(key);
if (it != g_ses_map.end()) {
printf("find key, update it\n");
it->second->copy(ses);
SAFE_DELETE(ses);
} else {
printf("not find key\n");
rc_pair = g_ses_map.insert(Ses_Map_Pair(key, ses));
printf("insert %s, status = %s, ses_map.size() = %d\n",
(rc_pair.first != g_ses_map.end()) ? "true" : "false",
rc_pair.second ? "was insert" : "was update",
(int)g_ses_map.size());
}
show_map();
printf("\n");
}
}
void free_map() {
Ses_Map_It it = g_ses_map.end();
printf("free map, ses_map.size() = %d\n",
(int)g_ses_map.size());
for (it = g_ses_map.begin(); it != g_ses_map.end(); it++) {
if (NULL != it->second) {
it->second->show_info();
}
SAFE_DELETE(it->second);
}
g_ses_map.clear();
}
void fn_map_usage_err() {
class_my_session* ses = NULL;
printf("\n\n");
printf(">> fn_map_usage_err\n");
// insert key1 and ses1
ses = gen_ses1();
insert_to_map_err(ses);
// insert key2 and ses2
ses = gen_ses2();
insert_to_map_err(ses);
// insert key3 and ses3
ses = gen_ses3();
insert_to_map_err(ses);
// free map
free_map();
/** run result
>> fn_map_usage_err
not find key
insert true, status = was insert, ses_map.size() = 1
name = [ses1] : ip = [11111] : port = [111]
find key
insert true, status = was update, ses_map.size() = 1
name = [ses1] : ip = [11111] : port = [111]
find key
insert true, status = was update, ses_map.size() = 1
name = [ses1] : ip = [11111] : port = [111]
free map, ses_map.size() = 1
name = [ses1] : ip = [11111] : port = [111]
*/
/** note
按照c++ reference的说法,如果key相同,会更新value
错误的map用法,引起了内存泄漏, 插入同一个key, 如果key存在, 会更新value.
当value是一个new出来的指针时,原始的指针被替换成新指针了,原始的指针就没人管了
map free时,也释放不到原始指针,引起内存泄漏
从实验结果看, 就是插入了3个指针,只释放了一个指针
我的实验[Red Hat Enterprise Linux Server release 5.4 (Tikanga)], 插入map失败,不替换指针.
如果不处理插入前的指针,那也是插入一回, 就丢了一个指针
假设key用的ip是服务器ip, port是服务的端口号.
不管是否在同一台计算机上用多个客户端去连服务,ip和port都是相同的, 即key相同
*/
}
void fn_map_usage_ok() {
class_my_session* ses = NULL;
printf("\n\n");
printf(">> fn_map_usage_ok\n");
// insert key1 and ses1
ses = gen_ses1();
insert_to_map_ok(ses);
// insert key2 and ses2
ses = gen_ses2();
insert_to_map_ok(ses);
// insert key3 and ses3
ses = gen_ses3();
insert_to_map_ok(ses);
// free map
free_map();
/** run result
>> fn_map_usage_ok
not find key
insert true, status = was insert, ses_map.size() = 1
name = [ses1] : ip = [11111] : port = [111]
find key, update it
name = [ses2] : ip = [11111] : port = [111]
find key, update it
name = [ses3] : ip = [11111] : port = [111]
free map, ses_map.size() = 1
name = [ses3] : ip = [11111] : port = [111]
*/
}