为什么编译器推迟std :: list deallocation?

时间:2011-11-08 12:00:24

标签: c++ stl memory-management dealloc

我有以下代码使用std :: list容器测试内存释放:

#include <iostream>
#include <list>
#include <string>

#include <boost/bind.hpp>

/* count of element to put into container
 */
static const unsigned long SIZE = 50000000;

/* element use for test
 */
class Element
{
public:
    Element()
    : mId(0)
    {}
    Element( long id )
    : mId(id)
    {}
    virtual ~Element()
    {
    }
    inline long getId() const
    {
        return this->mId;
    }

    inline bool operator<( const Element & rightOperand ) const
    {
        return this->mId < rightOperand.mId;
    }

    inline bool isEven() const
    {
        return 0 == ( this->mId & 1 );
    }

private:
    long mId;
};

typedef std::list< Element > Elements;

int main( int argc, char * argv[] )
{
    std::string dummy;
    {
        Elements elements;

        std::cout << "Inserting "<< SIZE << " elements in container" << std::endl;
        std::cout << "Please wait..." << std::endl;

        /* inserting elements
         */
        for( long i=0; i<SIZE; ++i )
        {
            elements.push_back( i );
        }

        std::cout << "Size is " << elements.size() << std::endl;
        std::getline( std::cin, dummy); // waiting user press enter

        /* remove even elements
         */
        elements.remove_if( boost::bind( & Element::isEven, _1 ) );

        std::cout << "Size is " << elements.size() << std::endl;
        std::getline( std::cin, dummy);
    }

    std::getline( std::cin, dummy);

    return 0;
}

运行此代码会为我提供以下内存配置文件:

MemoryProfile

看起来gcc正在推迟释放,在我的测试程序中,最终它没有选择并在返回命令行之前释放内存。

为什么这么晚发生了解除分配?

我尝试使用矢量来测试另一个容器,并且缩小到适合的技巧可以正常工作,并在我预期时释放释放的内存。

  

gcc 4.5.0,linux 2.6.34

4 个答案:

答案 0 :(得分:8)

大多数操作系统(包括Linux)只允许进程分配相当大的内存块,而不是非常小的内存;即使有可能,制作许多小型分配比大型分配更为昂贵。通常,C ++库将从操作系统中获取大块,并使用它自己的堆管理器将它们的一小部分分配给程序。一旦被分割出来,大块通常不会返回到操作系统;它们将继续分配给流程,并将重新用于将来的分配。

list以小块(每个节点一个)分配内存,因此通常在程序退出之前不会释放分配的内存。 vector 可能直接从操作系统将其内存作为单个大型分配获取,在这种情况下,它将在解除分配时释放。

答案 1 :(得分:2)

您的图表究竟是什么? std::list的析构函数 释放所有内存,以便它可以在其他地方重用 程序,但解除分配不一定会将内存返回给 系统,可以被其他进程使用。从历史上看,在 至少,在Unix下,一旦将内存分配给进程,就可以了 在该过程终止之前,该过程仍然存在。较新 算法可能能够实际将内存返回给操作系统,但即便如此 然后,像碎片这样的东西可能会阻止它这样做 - 如果 你分配,然后释放一个非常大的块,它可能会被返回,但如果 你分配了很多小块(这是std::list所做的), 事实上,运行时将从操作系统中分配大块 包裹出来;直到所有小块都无法返回这样的大块 在他们中被释放,即使在那时也可能不会被退回。

答案 2 :(得分:1)

这取决于您测量内存使用情况的方式。如果它正在测量正在使用的过程内存,那么这就是您可能真正期望的内容。

程序请求内存并将控制环境(例如操作系统)分配给进程是很常见的,但是当释放内存时,它不一定会被从进程中取走。它可以返回到过程中的空闲池

这是分配过去在古代工作的方式。对brksbrk的调用会通过为进程提供更多内存来增加堆的大小。该内存将被添加到满足malloc调用的竞技场中。

但是,free会将内存返回到竞技场,而不一定返回操作系统。

我想在这种情况下会发生类似的情况。

答案 3 :(得分:1)

您的内存配置文件实际上是进程的地址空间消耗(mmap - 页面的总和,例如,从进程本身的角度来看,由/proc/self/statm/proc/self/maps给出)。

但是当C或C ++函数使用malloc释放内存(以前使用newmmap分配,使用free从Linux内核获取内存时)或delete,它不会返回给系统(使用munmap - 因为这会太慢或不切实际[碎片化问题] - 但只能保留为将来malloc或{的可重复使用{1}}。

因此new请求时确实发生了解除分配,但内存未返回给系统,但保留以供将来重复使用。

如果您真的想要回馈内存,请编写自己的分配器(freemmap之上),但通常不值得。

也许使用Boehm's GC可以提供帮助(如果您明确地致电munmap,那么为了避免打扰free - 或delete -ing非常有用(但我我不确定那个,但你真的不应该那么在乎。

您的问题在技术上与GC_gcollect()无关(与其他C ++编译器相同)。它与Linux下的gccmalloc(即标准C&amp; C ++库)有关。