OSX 10.8上C ++中列表内存泄漏的向量

时间:2013-06-19 13:41:45

标签: c++ list memory vector stl

我在实现向量时遇到内存问题<列表<为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只返回对库的引用,而不是程序本身。

任何帮助都将非常感谢!!

干杯, 普拉门

2 个答案:

答案 0 :(得分:2)

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

我不熟悉OS X分配器的细节,但是分配器在较大的块中从操作系统获取内存然后将它们视为单独的池是很常见的。 这可能是你所看到的,当第一块记忆被填满时突然增长 您也可能在“较大”分配和“较小”分配之间传递一些阈值,并且您只是看到一个稍微小一点的添加池 - 一些分配器会这样做。
当然,原因也可能完全不同。

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

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

总结:内存分配器和操作系统现在非常复杂,你无法看到内存分配的增长,并且肯定地说你有内存泄漏。
在你的情况下,我会相信valgrind和乐器。

答案 1 :(得分: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.
};

但这不能回答你的问题。

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

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

要做一些更复杂的事情,分配和释放不同大小的内存会产生堆碎片,具体取决于分配/解除分配模式以及堆的实现方式。

物理内存不是此类程序中内存泄漏的良好指标。将是更好的私人(虚拟)记忆或类似记忆。