如何释放boost :: unordered_map所拥有的空闲内存?

时间:2012-05-31 07:11:20

标签: c++ memory unordered-map

我添加了大量元素,然后在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;
}

2 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分:1)

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

std::unordered_map

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

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

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

这使得在内存中有效地使用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存储库中上传了个人资料代码,以便您可以提供