1

我在实现向量< list< size_t>> 时遇到了内存问题。这是代码:

struct structure{
    vector< list<size_t>  > Ar;

    structure(int n){
        Ar.reserve(n);
        for(size_t i =0; (int) i<  n;i++){
            list<size_t> L;
            Ar.push_back(L);
        }
    }

    ~structure(){
       vector<list<size_t> >::iterator it = Ar.begin();
       while(it < Ar.end()){
           (*it).clear();
           ++it;
       }
       Ar.clear();
    }


};

int main() {

    for(size_t k = 0; k <100 ; k++){    
        structure test_s = structure(1000*(100 - k));
    }
    return 0;
}

随着时间的推移,分配给该程序的物理内存应该会减少,因为通过在构造函数中使用 100 - k 分配给 test_s 的内存越来越少。这不是我观察的!相反,物理内存在运行的一半左右增加!

由于我在一个占用大量内存的更大程序中使用此代码,所以这有点灾难!

有两个细节我觉得很奇怪:

首先,物理内存使用量没有逐步增加,即使对象的大小在 for 循环的每个阶段都在变化,而是在 for 循环的第 50 次迭代左右内存突然增加。这种情况在我每次跑步时都会发生(而且我已经做过很多次了!)。内存增加的迭代不是随机的!

其次,当我将静态 size_t(例如 10000)传递给结构(size_t)构造函数时,我不再遇到问题了。正如您可能猜到的,静态值对我的代码不是很有用,因为我需要能够动态分配结构对象的大小。

我在 macos 10.8.3 上使用 g++ 进行编译。我没有尝试在另一个平台上编译,因为我更愿意继续在我的 Mac 上工作。

我尝试过的所有内存管理工具(Apple Instruments 和 Valgrind)都不是特别有用。Valgrind 只返回对库的引用,而不是对程序本身的引用。

任何帮助将非常感激!!

干杯,普拉曼

4

2 回答 2

2

C++ 分配器在使用完内存后不一定会将内存返回给操作系统,但通常会保留它,因为您可能很快就会需要它。

我不熟悉 OS X 分配器的细节,但是分配器从操作系统中获取更大块的内存然后将它们视为单独的池是很常见的。
这可能就是您所看到的,随着第一块内存被填满,突然增长。
您也有可能在“更大”分配和“更小”分配之间通过了一些阈值,而您只是看到为稍微更小的东西增加了一个池 - 一些分配器会这样做。
当然,原因也可能完全不同。

当您为每个块使用相同大小时的差异很可能是因为分配器很容易使用最近释放的相同大小的块来填充请求。

当块具有不同的大小时,分配一个不同大小的新块比将一个空闲块分成两个较小的块更快。
(不幸的是,这也可能导致内存碎片。如果你得到许多分散的小块,可能会发生尽管总共有足够的空间但无法满足大的分配请求。)

总而言之:内存分配器和操作系统现在相当复杂,你不能看到内存分配的增长就确定你有内存泄漏。
在你的情况下,我会相信 valgrind 和 Instruments。

于 2013-06-19T14:07:03.507 回答
1

我没有看到该代码中有任何泄漏,但有很多不需要的代码。简化的代码是:

struct structure{
    vector< list<size_t>  > Ar;

    structure(int n): Ar(n) // initialize the vector with n empty lists
    {
    }

    // destructor unneeded, since Ar will be destroyed anyway, with all of its elements.
};

但这并不能回答你的问题。

堆内存分配并不意味着物理内存分配。现代操作系统使用虚拟内存,通常由分页文件支持。堆分配从虚拟内存中获取内存,操作系统决定是否需要更多或更少的物理内存。将内存释放到虚拟内存并不意味着释放物理内存(如果其他进程不需要,为什么要在那个时候这样做?)。

此外,堆内存分配不会直接转换为虚拟内存分配。通常,虚拟内存分配具有大粒度,因此不适合小分配。然后,堆管理器分配虚拟内存块并管理所有堆分配。(如果虚拟内存不够,堆管理器会要求更多)。何时释放未使用的虚拟内存块取决于堆管理器的实现方式。

做一些更复杂的事情,分配和释放不同大小的内存会产生堆碎片,这取决于分配/释放模式以及堆是如何实现的。

在此类程序中,物理内存不是内存泄漏的良好指标。会是更好的私有(虚拟)内存或类似内存。

于 2013-06-19T14:06:30.300 回答