由map.insert使用不当引起的内存泄漏

前言

有个写好的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]
    */
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值