2011-11-08 31 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以小塊(每個節點一個)分配內存,因此通常分配的內存不會在程序退出之前釋放。 vector可能直接從操作系統獲取其內存作爲一個單獨的大分配,在這種情況下,它將在釋放時釋放。

+0

Linux不會對分配的大小設置任何下限。另一方面,系統級別的分配可能相對昂貴,因此任何編寫良好的運行時都會避免在小塊中這樣做。 –

+0

@JamesKanze:我可能是錯的,但我認爲你一次只能分配少於一頁(通常是8k)。無論如何,你認爲從系統中做出很多小的分配將是非常昂貴的,所以即使你願意,你也不想。 –

+0

在我的系統上,Vector在退出範圍時釋放內存 – Nelstaar

2

你的圖表顯示的是什麼? std::list 的析構函數釋放所有內存,以便可以在 程序中的其他位置重新使用它,但是釋放內存不一定會將內存返回到其他進程可以使用的系統。從歷史上看,至少在 之下,在Unix下,一旦內存被分配給一個進程,其 保留在該進程中,直到進程終止。較新的 算法可能能夠實際將內存返回到操作系統,但即使如此,如碎片之類的事情可能會阻止它這樣做,如果 您分配,然後釋放一個非常大的塊,它可能會返回,但如果你 你分配很多小塊(這是std::list的作用), 運行時實際上將從操作系統分配大塊,它將 包裝出來;這樣的大塊不能被返回,直到其中的所有小塊 已經被釋放,並且甚至當時可能不會被返回。

1

這取決於你如何測量內存使用情況。如果正在測量正在使用的進程內存,這就是您實際期望的。

程序向程序請求內存並將其從控制環境(例如操作系統)分配給進程是很常見的,但是當內存被釋放時,它不一定會從進程中被帶走。它可能會返回到的免費池的過程中。

這是在過去的日子裏工作的方式。對brksbrk的調用將通過爲進程提供更多內存來增加堆的大小。該內存將被添加到來自malloc調用滿足的舞臺上。

但是,free會將內存返回到舞臺,而不一定返回到操作系統。

我想象在這種情況下發生了類似的事情。

1

您的內存配置文件實際上是進程的地址空間消耗(從過程本身的角度看,由/proc/self/statm/proc/self/maps給出的mmap -ed頁面的總和)。

但是,當C或C++函數釋放內存(先前mallocnew分配,其使用mmap獲得來自Linux內核的內存)使用freedelete,不還給系統(使用munmap - 因爲那將是太慢或不切實際[碎片問題] - 只是不斷的重複用於未來mallocnew

所以釋放做時free請求但內存不歸還給系統,而是保持發生未來再利用。

如果您真的想要回退內存,請編寫您自己的分配器(大於mmapmunmap),但通常不值得您付出努力。

也許使用Boehm's GC可以幫助(這是非常有用的,以免打擾約free -ing或delete -ing),如果您顯式調用GC_gcollect()(但我不肯定這一點),但你真的不應該想那麼多。

而你的問題在技術上與gcc沒有關係(它與其他C++編譯器相同)。它與Linux下的mallocnew(即標準C & C++庫)有關。

相關問題