我有以下代码使用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;
}
运行此代码会为我提供以下内存配置文件:
看起来gcc正在推迟释放,在我的测试程序中,最终它没有选择并在返回命令行之前释放内存。
为什么这么晚发生了解除分配?
我尝试使用矢量来测试另一个容器,并且缩小到适合的技巧可以正常工作,并在我预期时释放释放的内存。
gcc 4.5.0,linux 2.6.34
答案 0 :(得分:8)
大多数操作系统(包括Linux)只允许进程分配相当大的内存块,而不是非常小的内存;即使有可能,制作许多小型分配比大型分配更为昂贵。通常,C ++库将从操作系统中获取大块,并使用它自己的堆管理器将它们的一小部分分配给程序。一旦被分割出来,大块通常不会返回到操作系统;它们将继续分配给流程,并将重新用于将来的分配。
list
以小块(每个节点一个)分配内存,因此通常在程序退出之前不会释放分配的内存。 vector
可能直接从操作系统将其内存作为单个大型分配获取,在这种情况下,它将在解除分配时释放。
答案 1 :(得分:2)
您的图表究竟是什么? std::list
的析构函数
释放所有内存,以便它可以在其他地方重用
程序,但解除分配不一定会将内存返回给
系统,可以被其他进程使用。从历史上看,在
至少,在Unix下,一旦将内存分配给进程,就可以了
在该过程终止之前,该过程仍然存在。较新
算法可能能够实际将内存返回给操作系统,但即便如此
然后,像碎片这样的东西可能会阻止它这样做 - 如果
你分配,然后释放一个非常大的块,它可能会被返回,但如果
你分配了很多小块(这是std::list
所做的),
事实上,运行时将从操作系统中分配大块
包裹出来;直到所有小块都无法返回这样的大块
在他们中被释放,即使在那时也可能不会被退回。
答案 2 :(得分:1)
这取决于您测量内存使用情况的方式。如果它正在测量正在使用的过程内存,那么这就是您可能真正期望的内容。
程序请求内存并将控制环境(例如操作系统)分配给进程是很常见的,但是当释放内存时,它不一定会被从进程中取走。它可以返回到过程中的空闲池。
这是分配过去在古代工作的方式。对brk
或sbrk
的调用会通过为进程提供更多内存来增加堆的大小。该内存将被添加到满足malloc
调用的竞技场中。
但是,free
会将内存返回到竞技场,而不一定返回操作系统。
我想在这种情况下会发生类似的情况。
答案 3 :(得分:1)
您的内存配置文件实际上是进程的地址空间消耗(mmap - 页面的总和,例如,从进程本身的角度来看,由/proc/self/statm
或/proc/self/maps
给出)。
但是当C或C ++函数使用malloc
释放内存(以前使用new
或mmap
分配,使用free
从Linux内核获取内存时)或delete
,它不会返回给系统(使用munmap - 因为这会太慢或不切实际[碎片化问题] - 但只能保留为将来malloc
或{的可重复使用{1}}。
因此new
请求时确实发生了解除分配,但内存未返回给系统,但保留以供将来重复使用。
如果您真的想要回馈内存,请编写自己的分配器(free
和mmap
之上),但通常不值得。
也许使用Boehm's GC可以提供帮助(如果您明确地致电munmap
,那么为了避免打扰free
- 或delete
-ing非常有用(但我我不确定那个,但你真的不应该那么在乎。
您的问题在技术上与GC_gcollect()
无关(与其他C ++编译器相同)。它与Linux下的gcc
和malloc
(即标准C&amp; C ++库)有关。