我有一个程序将指向包含 cv::mat 克隆的结构的指针提供给其他线程使用的 std::queue/std::list (都尝试过)。
读取/创建速度很快,但消耗速度很慢,因此队列的大小会随着时间的推移而增长。
这个队列变得很大,很容易占用> 50% 的系统内存。
当读取/创建停止时,队列会缩小,但内存不会!
当队列大小最终达到 0 时,内存几乎立即消失。queue.size() == 0 触发器可以通过确保队列永远不会弹出最后一个指针来确认——内存不会消失。*注意:队列仍然存在,它没有超出范围,这是静态的。
所以我有两个问题:
为什么当队列大小为零时内存消失?或者换句话说,为什么当指针被消耗/删除时内存不会消失?
如何显式释放内存?
代码是这样的:
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 的内存管理,但它似乎在做类似的事情。暂停队列填充允许队列大小缩小,但内存不会缩小。然后,恢复填充会增加队列大小,而不会增加内存大小。
总结几个关键点:
std::queuestd::deque默认使用 a作为内部容器。当内存被释放时,实现在很大程度上定义了(当大小为零时可能会出现这种情况),但是std::deque和std::vector确实有一个释放多余内存的函数shrink_to_fit(即c++11功能)。这不能从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 实现,因为你说你也尝试过列表,至少在我的版本上,即使删除单个节点(如预期的那样),它似乎也会释放内存。