2013-04-16 51 views
2

我在堆地址增長上做了一些實驗,發生了一些有趣的事情。 (操作系統:CentOS,)分配更大空間時,爲什麼堆地址的增長方向會變得相反?

但我不明白,爲什麼發生這種情況?謝謝!

這是我做的第一:

double *ptr[1000]; 
for (int i=0;i<1000;i++){ 
    ptr[i] = new double[**10000**]; 
    cout << ptr[i] << endl; 
} 

輸出是增量(最後幾行):

.... 
.... 
0x2481be0 
0x2495470 
0x24a8d00 
0x24bc590 
0x24cfe20 
0x24e36b0 
0x24f6f40 
0x250a7d0 
0x251e060 

然後,我改變10000到20000:

double *ptr[1000]; 
for (int i=0;i<1000;i++){ 
    ptr[i] = new double[**20000**]; 
    cout << ptr[i] << endl; 
} 

地址變得更像堆棧空間的地址(和遞減):

.... 
.... 
0x7f69c4d8a010 
0x7f69c4d62010 
0x7f69c4d3a010 
0x7f69c4d12010 
0x7f69c4cea010 
0x7f69c4cc2010 
0x7f69c4c9a010 
0x7f69c4c72010 
0x7f69c4c4a010 
0x7f69c4c22010 
0x7f69c4bfa010 
0x7f69c4bd2010 
0x7f69c4baa010 
0x7f69c4b82010 

回答

4

你不會在這裏得到一個好的答案,因爲新函數可以選擇任何它想分配內存的方法。我的猜測是,這裏的算法將池分爲小型和大型分配池,而大分配池向下增長,因此它們可以在中間相遇(以免浪費任何空間)。

7

不同的環境/實現使用不同的策略分配內存,所以沒有一個正確的規則。然而,一個常見的模式是對小對象和大對象使用不同的分配策略。

通常,運行時將針對不同大小的對象具有多個堆,這些堆針對不同的使用模式進行了優化。例如,小對象傾向於經常分配和快速刪除,而大對象往往很少被創建並且壽命很長。

如果您對所有內容都使用單個堆,那麼會在整個內存空間中快速插入一些小對象,從而留下大量中等大小的塊,但大塊對象所需的塊很少或不需要大塊。這被稱爲內存碎片,並且可能會導致您的分配失敗,即使名義上您的應用程序有大量內存可用。

使用不同堆的另一個原因是對不同的對象大小使用不同的使用情況跟蹤方法。例如,一個實現可能會從OS請求一個用於大對象的新內存塊,對於小對象,則使用一些較小的OS內存塊以及由C運行時堆管理器處理的子分配。對大型對象非常有效的內存使用情況跟蹤機制對於較小的對象來說可能非常昂貴,因爲用於跟蹤使用情況的內存成爲每個對象所使用的實際內存的重要部分。

在你的情況下,我的猜測是運行時在內存空間的開始處自下而上分配小對象,而在靠近結束處自上而下分配較大的對象以避免碎片。

0

在UNIX上,分配器使用sbrk(2)和mmap(2)從OS獲取內存。由sbrk返回的地址已經定義好了,但是來自mmap的地址是「任何可用的」。在Windows上,分配器使用與mmap類似的VirtualAlloc()。

0

實現可以自由地具有不同分配方案的混合。在C++中,存在數以千計甚至數百萬個相對較小的對象是很正常的,所以它對於庫的內存分配例程來說是有意義的,以確保它們打包良好且非常輕量級。你的分配10000個雙打這樣做:它們相距80016字節 - 對於10000個8字節變量和只有16個字節的填充,80000個。特別是節點的大小與2的冪無關,而當分配20000加倍時,它們每次減少163840字節......奇怪地,正好是10 * 2^14。這對我來說,前面的分配是從一個堆中滿足的,這個堆被設計用來支持C++分配函數的有效的小對象分配,而後者已經越過了這個障礙,並且很可能被髮送到malloc以獲得來自不同堆的內存,浪費更多。

0

你很幸運,10000倍和20000倍的大小恰好位於稱爲MMAP_THRESHOLD的臨界閾值的相反側。

MMAP_THRESHOLD默認爲128KB。因此,80KB(即10000個雙打)mem分配請求在堆上服務,而160KB(20000個double)mem分配請求由匿名內存映射(通過mmap sys調用)提供服務。 (請注意,使用MEM映射大MEM頁頭可能會產生額外的處罰,由於其不同的底層MEM頁頭處理機制,您可能會需要調整MMAP_THRESHOLD爲您的應用程序的最佳性能。)

Linux Man for malloc

通常,malloc()從堆中分配內存,並使用sbrk(2)根據需要調整堆的大小。當分配大於MMAP_THRESHOLD字節的內存塊時,glibc malloc()實現使用mmap(2)將內存分配爲專用匿名映射。 MMAP_THRESHOLD默認爲128 kB,但可以使用mallopt(3)進行調整。使用mmap(2)執行的分配不受RLIMIT_DATA資源限制的影響(請參閱getrlimit(2))。

相關問題