2011-11-08 12 views
6

私がのstd ::リストコンテナを使用してメモリの解放をテストするために、次のコードを持っている:このコードを実行するなぜコンパイラは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

+0

どのようにメモリ消費量を測定していますか? – NPE

答えて

8
だけのプロセスがメモリのかなり大きな塊を割り当てることができ(Linuxを含む)、および非常に小さくないもの

ほとんどのオペレーティングシステム。たとえ可能であっても、いくつかの大きな割り当てよりも多くの小さな割り当てを行うほうが高価になる可能性が最も高いです。一般に、C++ライブラリはオペレーティングシステムから大きなチャンクを取得し、それ自身のヒープマネージャを使用して小さなプログラムをプログラムに割り当てます。大規模なチャンクは、通常、それが分割された後はオペレーティングシステムに戻されません。それらはプロセスに割り当てられたままであり、将来の割り当てに再利用されます。

listは、小さなチャンク(ノードごとに1つ)にメモリを割り当て、そのため通常は割り当てられたメモリは、プログラムが終了するまで解放されません。 vectorその割り当て解除するとき、それが解放される場合には、オペレーティング・システムから直接単一の大規模な割り当てなどのメモリを取得する可能性があります。

+0

Linuxは割り当てのサイズに下限を設定しません。一方、システムレベルでの割り当ては比較的高価なものになる可能性があるため、書かれたランタイムであれば、小さなブロックに対してはそうしないでください。 –

+0

@JamesKanze:私は間違っているかもしれませんが、一度に1ページ(通常8k)未満を割り当てることはできません。いずれにしても、システムから多くの小さな割り当てを行うのは非常にコストがかかるので、可能であれば、そうしたいとは思わないでしょう。 –

+0

私のシステムでは、スコープを終了するときにVectorからメモリを解放します。 – Nelstaar

2

あなたを示すグラフは正確には何ですか? std::list のデストラクタは、すべてのメモリを割り当て解除するので、 プログラムのどこかで再利用できますが、メモリを解放しても必ずしもメモリが他のプロセスで使用できる システムに返されるわけではありません。歴史的には、少なくとも であり、Unixではプロセスにメモリが割り当てられると、プロセスが終了するまでそのプロセスには が残っています。新しい アルゴリズムは実際にはメモリにOSを返すことができるかもしれませんが、たとえ であってもフラグメンテーションのようなことができない場合があります。 を割り当てると、本当に大きなブロックが解放されます。返されますが、 小さなブロック(多くはstd::list)の多くを割り当てると、 ランタイムは実際にはOSから大きなブロックを割り当てます。それは です。このような大規模なブロックは、それらのすべての小さなブロック まで戻すことはできません解放された、とそうでも返されることはありません。

1

どのようにメモリ使用量を測定しているかによって異なります。使用中のプロセスメモリを測定している場合、これは実際に予想されるものです。

プログラムがメモリを要求し、制御環境(オペレーティングシステムなど)からプロセスに割り当てられていることはかなり一般的ですが、メモリが解放されても必ずしもプロセスから取り出されるとは限りません。無料のプール内に戻すことができます。

これは、旧式の仕事で使用された割り当て方法でした。 brkまたはsbrkを呼び出すと、プロセスにさらに多くのメモリを割り当てることでヒープのサイズが大きくなります。そのメモリは、malloc呼び出しが満たされたアリーナに追加されます。

ただし、freeはメモリをアリーナに返すため、必ずしもオペレーティングシステムに戻ってくる必要はありません。

この場合も同様のことが起こっていると思います。

1

あなたのメモリプロファイルは、実際に(として例えばプロセス自体の観点から/proc/self/statm/proc/self/mapsで与えmmap -edページの合計)プロセスのアドレス空間の消費量です。

しかしときfreeまたはdeleteを使用してCまたはC++の機能のリリースメモリは、(以前のLinuxカーネルからメモリを取得するためにmmapを使用している、mallocまたはnewに割り当てられた)、それはmunmapを使用して(バックシステムに与えられていません - それは遅すぎるか非現実的[断片化の問題]になりますので、 - しかし、ちょうど将来mallocnewのための再利用可能なように保持

freeによって要求されたときに割り当て解除が起こったが、メモリをシステムに与えられていないが、保持しました。将来の再利用。

本当にメモリを戻したい場合は、独自のアロケータ(上記のmmapmunmap)を書きますが、通常はそれほど努力する価値はありません。

おそらく(私はそれを確認していない)明示的GC_gcollect()を呼び出す場合(約free -ingやdelete -ingを悩ま避けるために、それは非常に便利です)助けることができるが、あなたが本当にそれほど気にはならないBoehm's GCを使用して。

あなたの質問は、技術的にはgccに関連していません(別のC++コンパイラでも同じです)。 Linuxでは、mallocおよびnew(すなわち標準C & C++ライブラリ)に関連しています。

関連する問題