【问题标题】:How can I release the free memory held by boost::unordered_map?如何释放 boost::unordered_map 持有的空闲内存?
【发布时间】:2015-06-23 16:41:05
【问题描述】:

我添加了大量元素,然后在 boost::unordered_map 中将它们全部删除。 然后我看到这个程序占用的内存是198MB(大于(64+4)*2M),unordered_map大小是0。

然后我测试一个向量,没有这个问题。为什么?

#include <iostream>
#include <boost/unordered_map.hpp>

template <int N>
struct big_struct {
    char c[N];
};

int main(void) {
    typedef big_struct<64> data_type;
    typedef boost::unordered_map<int, data_type*> map_type;

    map_type m;

    for (int i = 0; i < 2000 * 1000; i++) {
            m.insert(std::make_pair(i, new data_type));
    }   

    for (map_type::iterator it = m.begin(); it != m.end();) {
            delete it->second;
            it = m.erase(it);
    }   

    std::cout << "finish, map size " << m.size() << std::endl;
    pause();

    return 0;
}

【问题讨论】:

  • 你有没有实际测试过是否存在内存泄漏?
  • 为了对两件事进行有意义的比较,需要同时看到这两件事。你是如何测试std::vector的? 我的测试显示 std::vectorboost::unordered_map 的行为与预期的完全相同。
  • @juanchopanza 该程序已通过 valgrind 测试,没有泄漏的可能。
  • 那么,没有什么可做的了。操作系统负责。

标签: c++ memory unordered-map


【解决方案1】:

语言运行时会保留分配的内存,假设您可能想再次使用它。将数百万个小块返回给操作系统需要相当长的时间,并且会使您的程序运行速度变慢。

如果你有一个非常大的向量,那仍然只是一个内存块。一些编译器考虑在不再需要时返回 this 类型的内存。返回一个大块比返回一百万个小块要高效得多。

【讨论】:

    【解决方案2】:

    这是一个明显的例子,说明当 boost 成为 C++ 的标准部分时会发生什么。

    std::unordered_map
    

    实际上缺乏对释放内存的控制,上述答案不正确

    rehash 发生之前,std::unordered_map 可能不会释放任何内存。当记录可能发生重新散列时,如果您查看size() 文档,它只是说它是元素的数量,如果您想要一个实际大小的想法,您必须在地图中添加一个自定义分配器并计算分配/解除分配字节。

    这很遗憾,因为由于没有记录该行为(也没有一些 API 来控制它),您不知道实现是否有可用内存(如果您暂时不使用地图,那就太好了),或者if 实现缓存内存(如果你要再次插入不同的元素很好)。

    这使得以内存有效的方式使用 unordered_map 非常困难。此外,这基本上为巨大的内存使用留出了空间而没有记录(没有任何地方说明没有元素的地图可能需要数百兆字节)

    这是一个自定义分配器,用于分析内存使用情况

    #include <unordered_map> //c++ container but in practice the same of boost
    #include <memory>
    #include <iostream>
    using namespace std;
    
    size_t counter = 0;
    
    template <typename T>
    class countingAllocator: public std::allocator<T>
    {
    public:
        typedef size_t size_type;
        typedef T* pointer;
        typedef const T* const_pointer;
    
        template<typename _Tp1>
        struct rebind
        {
                typedef countingAllocator<_Tp1> other;
        };
    
        pointer allocate(size_type n, const void *hint=0){
               counter += n;
                return std::allocator<T>::allocate(n, hint);
        }
    
        void deallocate(pointer p, size_type n){
                counter -= n;
                return std::allocator<T>::deallocate(p, n);
        }
    
        static size_t getAllocatedBytes() { return counter;}
    
        countingAllocator() throw(): std::allocator<T>() {}
        countingAllocator(const countingAllocator &a) throw(): std::allocator<T>(a) { }
        template <class U>                    
        countingAllocator(const countingAllocator<U> &a) throw(): std::allocator<T>(a) { }
        ~countingAllocator() throw() { }
    };
    
    template <int N>
    struct big_struct {
        char c[N];
    };
    
    template<
    
        class Key,
        class T,
        class Hash = std::hash<Key>,
        class KeyEqual = std::equal_to<Key>,
        class Allocator = std::allocator< std::pair<const Key, T> >
    > class unordered_map;
    
    int main( int argc, char ** argv) {
        typedef big_struct<64> data_type;
        typedef std::unordered_map<int, data_type*, std::hash<int>, std::equal_to<int>, 
                                countingAllocator< std::pair< const int, data_type*>> > map_type;
    
        map_type m;
    
        for (int i = 0; i < 1000 * 1000; i++) {
                m.insert(std::make_pair(i, new data_type));
        }   
    
        for (map_type::iterator it = m.begin(); it != m.end();) {
                delete it->second;
                it = m.erase(it);
        }   
    
        std::cout << "allocated memory before returning " << countingAllocator< std::pair< const int, data_type*>> ::getAllocatedBytes() << std::endl;
    
        return 0;
    }
    

    以及程序的输出:

    allocated memory before returning 1056323
    

    所以基本上这意味着您需要以某种方式调用 map 析构函数以正确清除之前分配的内存,您可以通过多种方式做到这一点:

    • 将 unordered_map 放入shared_ptr
    • 使 unordered_map 超出范围

    我将个人资料代码上传到我的PublicProfileTests 存储库,以便您可以贡献

    【讨论】:

      猜你喜欢
      • 2017-06-26
      • 2017-01-11
      • 1970-01-01
      • 1970-01-01
      • 2014-09-12
      • 1970-01-01
      • 2015-07-06
      • 1970-01-01
      • 2021-11-26
      相关资源
      最近更新 更多