在用户定义的类中清空 std::vector 时不释放内存

Memory not released when emptying std::vector inside a user-defined class

本文关键字:vector 内存 释放 std 清空 定义 用户      更新时间:2023-10-16

std::vector是类的字段时,我们遇到了一些内存问题。我们用大量数据填充此向量,这些数据在程序的某个点需要发布。但是,即使矢量容量为零,也不会释放或完全释放内存。

在这里,您有我们程序的简化版本。如您所见,类Foo只有一个字段:std::vector<int> 。如果我们创建一个std::vector<Foo>并用Foo对象填充它,当我们清空每个对象内部的向量时,内存不会完全释放。

我们已经使用活动监视器测量了内存使用情况,您可以在每个日志行旁边看到每个阶段使用的字节数。此外,我们还添加了另一个不使用类Foo对象的版本,在这种情况下,内存被完美释放。

#include <iostream>
#include <vector>
class Foo {
public:
    std::vector<int> container;
};
int main() {
    int n1 = 1000;
    int n2 = 100000;
    {
        std::vector<Foo> foos;
        std::cerr << "starting" << std::endl; // 160 KiB
        std::cin.get();
        for (int i = 0; i < n1; i++){
            Foo foo;
            foo.container.assign(n2, 666);
            foos.push_back(foo);
        }
        std::cerr << "foos filled" << std::endl; // 382.1 MiB
        std::cin.get();
        for (unsigned int i = 0; i < foos.size(); i++){
            std::vector<int>().swap(foos[i].container);
        }
        std::cerr << "foos emptied" << std::endl; // 195.7 MiB
        std::cin.get();
    }
    std::cerr << "foos destroyed?" << std::endl; // 296 KiB
    std::cin.get();
    {
        std::vector<std::vector<int> > foos;
        std::cerr << "starting" << std::endl; // 296 KiB
        std::cin.get();
        {
            std::vector<int> aux;
            aux.assign(n2, 666);
            foos.assign(n1, aux);
        }
        std::cerr << "foos filled" << std::endl; // 382.1 MiB
        std::cin.get();
        for (unsigned int i = 0; i < foos.size(); ++i) {
            std::vector<int>().swap(foos[i]);
        }
        std::cerr << "foos emptied" << std::endl; // 708 KiB
        std::cin.get();
    }
    std::cerr << "foos destroyed?" << std::endl; // 708 KiB
    std::cin.get();

    return 0;
}

如果有帮助,我们将在 Ubuntu 14.04 64 位下使用 g++ 4.8.4。具体的内存占用取决于我们使用 C++11 还是 C++98,但在这两种情况下都会发生相同的现象。

关于正在发生的事情以及如何恢复该记忆的任何想法,如果需要,可以强行恢复?

编辑:请注意,当我们销毁Foo类的所有对象时,内存实际上大部分都会返回,但是在我们的现实问题中,我们仍然需要Foo模拟类的其余内容。

内存从运行时C++/C 库释放到用户空间内存分配器。通常,这并不意味着用户空间分配器会将此内存返回给操作系统。 用户空间分配器按块从内核分配内存。这些块通过new/malloc进一步根据您的请求进行切片。当您释放/删除此切片块时,它们将返回到用户空间分配器,而不是内核。而当用户空间分配器将能够将分配的内存块返回给内核时,只有用户空间分配器知道。

@user1641854的答案是正确的。这个答案是关于修复它。

有一种

相对简单的方法可以解决您的问题。您可以为您的向量提供一个allocator,该在内部直接要求操作系统提供内存,并在释放时直接将其释放回操作系统。这通常是不可取的,因为直接从操作系统分配通常比设计良好的用户模式堆更慢的分配/释放速度,并且您会在页面末尾浪费一些内存。此外,如果不付出一些努力,它就不会是跨平台的。

话虽如此,您的情况似乎是尝试这是一件合理的事情。

请参阅此处了解如何定义自己的分配器。

然后在Windows上使用::VirtualAlloc/::VirtualFree或在Linux上使用mmap/munmap用于底层分配/自由函数。