2010-10-01 80 views
4

我執行在Linux下我自己的圖形庫(Fedora 10中和CentOS 5)用gcc 4.3.2和使用STL容器,然後我發現了一些問題與記憶。當我構建我的圖形時,我使用了足夠多的內存來查看top或其他內存使用工具。我敢肯定,我重新分配內存(我一次又一次地回顧了代碼,我使用的valgrind檢查內存泄漏),但內存仍然在使用(我可以在頂部執行cat/proc查看此/ meminfo),當我再次創建圖形時,它不會增加內存使用量,顯然會重新使用分配的內存。STL容器內存問題

經過幾天的調試,我創建了一個非常簡單的代碼,它有相同的問題。

#include <iostream> 
#include <list> 

// Object that occupies 128KB. 
// Data is not important. 
class MyObject 
{ 
public: 
    int * a; 
    int * b; 
    int * c; 
    int * d; 

    MyObject() 
    { 
     a = new int[ 8192 ]; 
     b = new int[ 8192 ]; 
     c = new int[ 8192 ]; 
     d = new int[ 8192 ]; 
    } 

    MyObject(const MyObject & m) 
    { 
     a = new int[ 8192 ]; 
     b = new int[ 8192 ]; 
     c = new int[ 8192 ]; 
     d = new int[ 8192 ]; 
    } 

    ~MyObject() 
    { 
     delete [] a; 
     delete [] b; 
     delete [] c; 
     delete [] d; 
    } 

    void operator=(const MyObject &m) 
    { 
     //Do nothing. 
    } 
}; 

typedef std::list<MyObject> list_t; 

#define MB_TO_ALLOC 1000 // Size in MB that the program must alloc. 

#define SLEEP_TIME 5  // Time in seconds that the program must wait until go to another step. 
         // It's used to give sufficient time for tools update the memory usage 

int main() 
{ 
    std::cout << "Alloc..." << std::endl; 

    list_t * list = new list_t(); 

    // Number of objects for alloc MB_TO_ALLOC amount of memory 
    int nObjects = MB_TO_ALLOC * 1024/128; 

    for(int i = 0; i < nObjects; ++i) 
     list->push_back(MyObject()); 

    std::cout << SLEEP_TIME << "s to Dealloc..." << std::endl; 

    // Wait some time for a tool (like top) to update the memory usage 
    sleep(SLEEP_TIME); 

    std::cout << "Dealloc..." << std::endl; 

    delete list; 

    std::cout << SLEEP_TIME << "s to Alloc..." << std::endl; 

    // Wait some time for a tool (like top) to update the memory usage 
    sleep(SLEEP_TIME); 

    //Repeats the procedure for evaluating the reuse of memory 
    std::cout << "Alloc..." << std::endl; 

    list = new list_t(); 

    for(int i = 0; i < nObjects; ++i) 
     list->push_back(MyObject()); 

    std::cout << SLEEP_TIME << "s to Dealloc..." << std::endl; 

    sleep(SLEEP_TIME); 

    delete list; 
} 

我試圖用簡單的數組或我自己的列表類,但在這些情況下,內存通常釋放。

有誰知道發生了什麼事?如何防止這個內存被「保留」?

謝謝!

- 布魯諾卡波尼

+2

請注意,當你分配在車隊內存和可用它在析構函數,你的代碼是不是異常安全的,因爲四個分配中的任何一個可能失敗(如果,例如,第三分配失敗,對象'一'和'b'將被泄漏)。在這種情況下最好使用像boost :: scoped_ptr這樣的智能指針(它有一個非常簡單的實現)。 – 2010-10-01 18:46:09

+2

@James在這裏我只是簡單地使用表格:沒有必要爲這個演示動態地分配它們。也不需要動態地分配'list'。恐怕'Bruno'來自Java/C#背景,非常需要使用'new'。 – 2010-10-01 18:58:52

+2

'MyObject'需要一個賦值操作符。否則,您將刪除相同的內存塊兩次。 – 2010-10-01 19:00:17

回答

2

用於容器類內存使用通過處理容器的allocator(其被傳遞在作爲構造參數std::list,並且是由std::allocator默認值)。默認分配器的實現可以選擇不立即將內存返回給系統,以防止堆的過度分段。

如果你想在這個更直接的控制,你可能不得不實現自定義分配器。

+0

我調試默認的分配器,顯然內存應該正常返回到系統。然後我使用類似於標準分配器(:: operator new()和placement new)的策略實現自己的分配器和我自己的列表,並且不會出現問題。 – 2010-10-01 19:18:26

4

GCC STL有抓住的內存大塊,不給他們回了自己的內存管理層;有你可以設置讓它使用原始新呼叫

GLIBCPP_FORCE_NEW=1 

我認爲這使得自由太一個環境變量。這個env var通常在使用valgrind時使用,以便valgrind不會認爲泄漏的東西

+1

ps。 env var名稱改變了幾次。谷歌會告訴你哪個名稱是由哪個gcc版本使用 – pm100 2010-10-01 18:50:11

+0

我在google上發現該環境變量是GLIBCXX_FORCE_NEW,但我已經嘗試過,但仍然沒有返回內存。你使用Linux?你可以在你的機器上測試嗎? – 2010-10-01 19:36:27

+0

是的Linux。不,我不會測試(sry) – pm100 2010-10-01 21:17:22

3

除了STL容器,它可能是libc自己這樣做的(new/delete-malloc/free實現)。用戶空間庫可以自由保留內存以備後用。分配/解除分配是一項昂貴的操作(在時鐘週期方面),因此很多實現嘗試避免這種情況。

0

在Linux用戶存儲器通過系統調用BRK,向下延伸的數據指針,從而使可用於處理更多的RAM分配給進程從內核。這是內核將正常內存傳遞給進程的唯一方式。也可以通過使用mmap來獲得內存,它允許進程指定一個起始地址(除了數據指針),但沒有分配器這樣做,因爲它極大地增加了分配器和內核分配器必須的工作量做。由於這個原因,給內核用戶的內存很容易被內核收回,直到進程終止。如果您的特定應用程序正在執行大量的alloc/deallocs,那麼使用mmap來執行這些分配可能是一種解決方案,可以將內存映像大小降低一些。

0

我不能肯定地說會發生什麼,但問題(如果它是一個問題)是可重複的。

我認爲這是一個內存池的問題,即徘徊在STL中使用的特定分配器這樣做。 向下鑽取列表<>,不過,我發現,做什麼更多的,全球新的運營商的返回結果只是一個「new_allocator」:

pointer 
allocate(size_type __n, const void* = 0) 
{ return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); } 

行爲去然後到glibc或stdlibC++內存處理,我的理解。 在我快看我無法弄清楚如何circunvent這種行爲,但並不實現自定義分配器也不如果自定義的分配器肯定會表現不同。

我試過,沒有STL另一個測試,然後我可以看到越來越多的資源上下。 我會建議創建一個自定義分配器,用於分配數組中的任意數量的元素,並關心分配/取消分配這些數組。邏輯告訴我,資源使用必須像「非STL」測試一樣。

請您嘗試一下,告訴我們發生了什麼。我不會做我自己,因爲我現在沒有時間的空閒時間,儘管我好學的;)

注:「三巨頭」的規則在這裏有沒有影響。據我所知,沒有內存泄漏該對象的內容是不相關的。布魯諾可以完成數據複製,自我分配檢查等,但只是做了一個空的拷貝構造函數來說明它的觀點。

0

我遇到同樣的問題,經過長時間的調試寫了示例程序,其中ilustrates,這是一些內核(或者G ++)的問題。

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <ctime> 

static const size_t CHUNKS_COUNT = 1024 * 1024; 
void* chunks[CHUNKS_COUNT]; 

int main(int argc, const char *argv[]) { 
    bool additionalAlloc = false; 
    if (argc > 1) 
    additionalAlloc = true; 

    fprintf(stdout, "%lu: starting allocating chunks, additionalAlloc=%d\n", 
    time(NULL), additionalAlloc); 

    for (size_t n = 0; n < 2; ++n) { 
    void* additionalChunk; 

    // 1GB 
    for (size_t c = 0; c < CHUNKS_COUNT; ++c) { 
     static const size_t BASE_CHUNK_SIZE = 1024; 
     chunks[c] = malloc(BASE_CHUNK_SIZE); 
    } 

    if (additionalAlloc) { 
     // 33 is arbitrary, but for instance for 123 given example 
     // is not working - magic :-) 
     additionalChunk = malloc(33); 
    } 

    fprintf(stdout, "%lu: finished allocating chunks, n=%lu\n", 
     time(NULL), n); 
    sleep(60); 

    for (size_t c = 0; c < CHUNKS_COUNT; ++c) { 
     free(chunks[c]); 
    } 

    if (additionalAlloc) 
     free(additionalChunk); 

    fprintf(stdout, "%lu: finished freeing chunks, n=%lu\n", 
     time(NULL), n); 
    sleep(60); 
    } 

    sleep(60); 

    fprintf(stdout, "%lu: finishing program\n", time(NULL)); 

    return 0; 
} 

當它在沒有參數的情況下運行(additionalAlloc爲false)時,內存在調用free後釋放給系統。但是,當它使用參數運行(additionalAlloc爲true)時,只有在程序結束後才能釋放內存。我在2.6.18-6-xen-amd64內核上使用4.4.5-1 g ++在Debian Squeeze上運行它。我不知道它是如何在其他系統上工作的,但是看到123個字節用於其他系統會導致不同的程序行爲,很有可能它不起作用 - 但請相信我對於這些設置起作用:-)

PS。任何人都可以解釋爲什麼33和123的額外塊會導致不同的行爲?