为什么std :: queue在弹出元素后不缩小内存?

时间:2016-07-05 08:27:44

标签: c++ queue

我写了一个使用std :: queue

的小程序
queue<double> the_queue;

for(int i = 0; i < 100; i++)
{
    for(int j = 0; j < 5000; j++)
    {
        double d = 1.0 * (rand() % 1000);
        the_queue.push(d);
    }
}

printf("Done pushing\n");

while(!the_queue.empty())
{
    the_queue.pop();
}

printf("Done popping\n");

我在printf("Done pushing\n");printf("Done popping\n");放置2个断点,并在命中断点时检查程序的内存使用情况(在任务管理器中显示)。在Done pushing,内存使用量约为34 MB,但在Done popping时,内存使用量仍为~34 MB。这让我感到惊讶!

这是为什么?有没有办法克服这个问题?

4 个答案:

答案 0 :(得分:7)

基本上std::queue适配器容器 - 它本身不是容器,而是围绕其他容器的薄包装。

例如,让我们看一下队列签名:

template <class T, class Container = deque<T> > class queue;

如您所见,T是存储在队列中的元素的类型,Container是底层容器。

这就是你的问题的答案:不同的容器处理内存的方式不同。潜在的双端队列可能会缩小,也可能不会缩小,但是由deque内部来决定。

您也可以使用std::list作为基础容器。在这种情况下,每个pop删除基础列表节点内存。

您也可以自己编写或修改现有容器以匹配您自己的内存管理模式。您的容器需要支持一些方法(吮吸为push_backpop_front),您可以在相关的在线文档中阅读。

以下是deque适配器的示例,该适配器每1024次pop次呼叫收缩容量:

template<class T>
class DequeAdapater{

private:
    std::deque<T> m_Deque;
    size_t m_PopCount;

public:
    DequeAdapater():
        m_PopCount(0){}

    bool empty() const noexcept{
        return m_Deque.empty();
    }

    size_t size() const noexcept{
        return m_Deque.size();
    }

    T& front() noexcept{
        return m_Deque.front();
    }

    const T& front()const noexcept{
        return m_Deque.front();
    }

    T& back() noexcept{
        return m_Deque.back();
    }

    const T& back()const noexcept{
        return m_Deque.back();
    }

    void push_back(const T& t){
        return m_Deque.push_back(t);
    }

    void push_back(T&& t){
        return m_Deque.push_back(std::move(t));
    }

    void pop_front(){
        m_Deque.pop_front();
        m_PopCount++;
        if (m_PopCount%1024U == 0U){
            m_Deque.shrink_to_fit();
        }
    }

}


template <class T>
using LeanQueue = std::queue<T,DequeAdapater<T>>;

请注意,容量缩小意味着将队列元素移动或复制到新的瘦块,内存消耗会更小,但性能可能会降低。

答案 1 :(得分:5)

当队列超出范围时,队列管理的任何内存都将被释放。

然而,即使这样,内存也可能无法释放回操作系统,因为标准库假设如果您之前使用过内存,则可能需要再次使用它。

在程序链接的特定c运行时库中,malloc / free中的具体内容将被处理。

这是一个内存紧张的嵌入式系统吗? (在这种情况下可能考虑固定大小的容器),还是在服务器/桌面/ ipad上运行? (在这种情况下告诉你的老板不要再担心他不理解的事情了。)

答案 2 :(得分:1)

对于经常反复使用的容器,分配和释放内存可能会成为一个很大的开销。因此, containers 允许增长到它们使用的任何程度。如果要回收内存,则必须明确地执行此操作。通常这涉及允许容器超出范围,因为标准(由于某种原因)不提供释放内存的功能。有一个函数可能/可能不会在某些容器(shrink_to_fit())上释放未使用的内存,但不能保证。

幸运的是,有一个习惯用于将容器减少到开始量的内存,称为 swap和reset

您基本上使用工作容器创建一个新的容器和swap()其(空)内容。

// create a temporary empty container and swap its
// contents with the working container
std::queue<double>().swap(q);

答案 3 :(得分:0)

因为pop只是缩小了它的大小,而不是容量。

然后,如果你将其他元素放入队列中,它就不必分配内存,它只会使用已经分配的内存。