为什么这个 std::queue/list of pointers to structs 直到 List.Size() == 0 才释放内存?

Mar*_*ars 6 c++ opencv

我有一个程序将指向包含 cv::mat 克隆的结构的指针提供给其他线程使用的 std::queue/std::list (都尝试过)。

读取/创建速度很快,但消耗速度很慢,因此队列的大小会随着时间的推移而增长。
这个队列变得很大,很容易占用> 50% 的系统内存。

当读取/创建停止时,队列会缩小,但内存不会!
当队列大小最终达到 0 时,内存几乎立即消失。queue.size() == 0 触发器可以通过确保队列永远不会弹出最后一个指针来确认——内存不会消失。*注意:队列仍然存在,它没有超出范围,这是静态的。

所以我有两个问题:

  1. 为什么当队列大小为零时内存消失?或者换句话说,为什么当指针被消耗/删除时内存不会消失?

  2. 如何显式释放内存?


代码是这样的:

struct MyStruct {
    cv::mat matA;
    ~MyStruct(){
       cout << "Yeah, destructor is called!" << endl;
       //matA.release(); //Not needed, right? Adding it does not change anything.
    }
};

static queue<shared_ptr<MyStruct>> awaitingProcessing;
static mutex queueGuard;
Run Code Online (Sandbox Code Playgroud)

线程 1(队列填充器):

BYTE* buffer = (BYTE*)malloc(dataWidth*dataHeight);
while(signal){
    LoadData(buffer);
    cv::Mat data = cv::Mat(dataHeight, dataWidth, CV_8U, buffer);
    
    auto addable = shared_ptr<MyStruct>(new MyStruct())>;
    addable->matA = data.clone();
    lock_guard<mutex> guard(queueGuard);
    awaitingProcessing.push(addable);

}
Run Code Online (Sandbox Code Playgroud)

线程 2(消费者):

    shared_ptr<MyStruct> pullFromQueue(){
        lock_guard<mutex> guard(queueGuard);
        if (awaitingProcessing.size() > 0){
            auto returnable = awaitingProcessing.front();
            awaitingProcessing.pop();
            return returnable;
        }
        return nullptr;
    }

    void threadLogic(){
        while (!interruptFlag){
            auto ptr = pullFromQueue();
            if (ptr == nullptr){
                usleep(5);
            }
            else{
                doSomething(ptr);
            }
            // ptr destructor called here, as refcount properly hits zero
        } 

    }

Run Code Online (Sandbox Code Playgroud)

如果我没记错的话,std 数据集合通常不会释放它们的内存并将其保留为备用,以防大小再次增长。然而,这个集合(队列和/或列表)由指针组成,所以即使队列变大,内存占用也应该很小。

我不熟悉 OpenCV 的内存管理,但它似乎在做类似的事情。暂停队列填充允许队列大小缩小,但内存不会缩小。然后,恢复填充会增加队列大小,而不会增加内存大小。


总结几个关键点:

  • 内存确实在不改变范围的情况下被释放(即不是内存泄漏)
  • 只有当队列大小达到零时才会释放内存。如果队列大小永远保持为 1,则不会释放
  • 结构被破坏
  • 结构包含克隆的 cv::mats (我认为这是关键点)
  • 列表/队列只包含指针,因此应该很小

dar*_*une 1

std::queuestd::deque默认使用 a作为内部容器。当内存被释放时,实现在很大程度上定义了(当大小为零时可能会出现这种情况),但是std::dequestd::vector确实有一个释放多余内存的函数shrink_to_fit(即功能)。这不能从std::queue接口中获得,但可以通过继承来完成(队列中的容器是protected)。

伪代码

template<class T>
struct shrinkable_queue : public std::queue<T> {
  void shrink_to_fit() {c.shrink_to_fit();}
};
Run Code Online (Sandbox Code Playgroud)

您还可以使用std::queue<T, std::list<T>>. 我确实检查了 MSVC 实现,因为你说你也尝试过列表,至少在我的版本上,即使删除单个节点(如预期的那样),它似乎也会释放内存。