• C++ map clear内存泄漏问题


    map值存的是指针

    map自带的clear()函数会清空map里存储的所有内容,但如果map值存储的是指针,则里面的值不会被清空,会造成内存泄漏,所以值为指针的map必须用迭代器清空。

    使用erase迭代删除
    迭代器删除值为指针的map,一定要注意迭代器使用正确,一旦迭代器失效程序就会崩溃。

    std::map<int, HHH*> test_map;
    HHH* h1 = new HHH;
    HHH* h2 = new HHH;
    test_map[0] = h1;
    test_map[1] = h2;
    
    
    // 删除
    std::map<int, HHH*>::iterator iter;
    for (iter = test_map.begin(); iter != test_map.end();)
    {
        delete iter->second;
        iter->second = nullptr;
        // 删除迭代器元素先加加再删,否则迭代器失效程序崩溃!!!(必须iter++不可以++iter)
        test_map.erase(iter++);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    map值存储的不是指针

    std::map<int,int> test_map;
    test_map[0] = 0;
    test_map[1] = 0;
    
    // 删除
    test_map.clear(); //值为指针不要这样删除
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    调用clear()函数之前先把值里的指针的值通过迭代器delete

    	std::map<int, HHH*> test_map;
        HHH* h1 = new HHH;
        HHH* h2 = new HHH;
        test_map[0] = h1;
        test_map[1] = h2;
    
        // 删除
        std::map<int, HHH*>::iterator iter;
        for (iter = test_map.begin(); iter != test_map.end();)
        {
            delete iter->second;
            iter->second = nullptr;
            // 删除迭代器元素先加加再删,否则迭代器失效程序崩溃!!!(必须iter++不可以++iter)
            iter++;
        }
        test_map.clear();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    map中存储的是智能指针

    若是采用了智能指针,则无需单独delete,智能指针,会自动释放内存

    std::map<int, std::shared_ptr<int>> m_map;
    m_map[0] = std::make_shared<int>();
    delete m_map[0]; //错误
    
    • 1
    • 2
    • 3

    清空map释放内存

    若需要多次使用同一个map,其中每次使用后都clear清空,多次之后,可能出现内存泄露,这是因为map的空间便没有释放,所以得使用swap清空。

    如果内存错误提示如下

    Program terminated with signal SIGSEGV, Segmentation fault.
    #0  0x00000000010ca227 in tcmalloc::SLL_Next(void*) ()
    (gdb) bt
    #0  0x00000000010ca227 in tcmalloc::SLL_Next(void*) ()
    #1  0x00000000010ca2b8 in tcmalloc::SLL_TryPop(void**, void**) ()
    #2  0x00000000010ca715 in tcmalloc::ThreadCache::FreeList::TryPop(void**) ()
    #3  0x00000000011ebe6c in tc_newarray ()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    STL容器调用clear()方法,通常只是使得容器内部的对象通通析构,但容器本身的内存无法得到释放。即篮子里面东西拿走了,篮子占的空间还在,这样是为了方便下次存放新的对象时,不需要再次申请空间。即clear()后,容器的size为0,但capacity不变。通过swap()空容器,来彻底释放容器占用的capacity。

    #include
    #include
    #include
    #include 
    #include 
    using namespace std;
     
    class useTest
    {
    public:
        useTest() {};
        map<string,string> testMap;
        vector<string> testVertor;
        string id;
    };
     
    void clearData(map<int, useTest>& needClearMap)
    {
        clock_t  startt = clock();
     
        //分别通过去注释测试下面四种情况
     
        //使用clear
        //needClearMap.clear();
     
        //使用swap
        map<int, useTest> uu;
        needClearMap.swap(uu);
     
        //使用erase
        //needClearMap.erase(needClearMap.begin(), needClearMap.end());
     
        //使用for erase
        //for (auto iter = needClearMap.begin(); iter != needClearMap.end(); iter = needClearMap.erase(iter)) {}
        double sec = double(clock() - startt) / CLOCKS_PER_SEC;
        std::cout << "In Clear Cost Time:" << sec << endl;
    }
     
    void test()
    {
        map<int, useTest> needClearMap;
        for (size_t i = 0; i <= 10000; ++i)
        {
            useTest uT;
            for (size_t ii = 0; ii <= 1000; ++ii)
            {
                uT.testMap[to_string(ii)] = "我是测试,我是测试,我就是测试string";
                uT.testVertor.push_back("我也是测试,我也是测试,我就是测试string");
            }
            uT.id = to_string(i);
            //cout << i << endl;
            needClearMap[i] = uT;
        }
        clock_t  startt = clock();
        clearData(needClearMap);
        double sec = double(clock() - startt) / CLOCKS_PER_SEC;
        std::cout << "clearData Cost Time:" << sec << endl;
    }
     
    int main()
    {
        for (size_t i = 0; i < 10; ++i)
        {
            test();
        }
        getchar();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    就单单实现某个map清空来说,swap效率最高,几乎是0耗时。但是当退出整个函数,释放swap转移到的临时对象要耗一定的时间。erase效率稍微比clear高。通过for循环erase好似效率又高点。

    对于map、set、unordered_map等容器,调用clear()、swap()都无法使得内存真正释放。虽然很多地方谈到,这一现象(内存被保留下来)是正常的,并不需要担心。但是当大量使用堆内存存放不同的数据结构,会造成严重的内存碎片从而导致内存泄漏问题。

    #include 
    #include 
    #include 
    using namespace std;
    void func()
    {
            map<int,string> mp;
            int i = 5000000;
            while(i--)
                mp.insert(make_pair(i,string("hell000o")));
            map<int,string>().swap(mp); //swap
    }
    int main()
    {
            func();
            cout <<"done."<<endl;
            malloc_trim(0);
            while(1);
    }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    只需添加一行,malloc_trim(0); 这一行代码会将空闲的堆内存归还给操作系统,供其他进程使用。

  • 相关阅读:
    maven仓库改国内源
    黑客(网络安全)技术自学30天
    竞赛 基于设深度学习的人脸性别年龄识别系统
    栈与队列2——用栈实现队列
    Jdbi 指南
    Python基础知识从hello world 开始(第一天)
    4.4亿赎金,6800GB数据窃取,勒索病毒攻击到底怎么防?
    Spring Security(五)--管理用户
    如何用 Elasticsearch 实现 Word、PDF,TXT 文件的全文内容检索?
    操作系统复习:死锁
  • 原文地址:https://blog.csdn.net/qq_36314864/article/details/132716800