2014-09-01 35 views
2

我想寫一些單元測試,檢查內存是否已被釋放 - 檢查內存泄漏 - 在OS X(10.9小牛)上。我試圖使用mstats()和malloc_zone_statistics()來發現我需要的信息。但似乎他們不顯示內存被釋放(見下面的示例輸出...內存使用免費後不會改變()被調用)爲什麼mstats和malloc_zone_statistics在空閒後不顯示恢復的內存?

我懷疑這比具有更多的是與堆管理這些功能的問題。我認爲這堆並不釋放釋放的內存,也許它可以在沒有刪除和添加塊的開銷的情況下重用它。

  1. 有沒有辦法告訴堆釋放釋放塊?要更積極或關閉優化?
  2. 我只是不正確地使用mstats()或malloc_zone_statistics()?

更新:溶液中發現的......在底部提供...

這裏是我的測試程序的輸出:

=== Initial conditions === 
in use: 23584, allocated: 9437184, blocks: 320 
SimpleLeaker(19583,0x7fff7b2a2310) malloc: total: 9437184, used: 23584, free: 9413600 
=== Before allocation === 
in use: 23584, allocated: 9437184, blocks: 320 
SimpleLeaker(19583,0x7fff7b2a2310) malloc: total: 9437184, used: 23584, free: 9413600 
=== After malloc === 
in use: 33824, allocated: 9437184, blocks: 321 
SimpleLeaker(19583,0x7fff7b2a2310) malloc: total: 9437184, used: 33824, free: 9403360 
=== After free === 
in use: 33824, allocated: 9437184, blocks: 321 
SimpleLeaker(19583,0x7fff7b2a2310) malloc: total: 9437184, used: 33824, free: 9403360 

這裏是爲C代碼程序:

#include <stdio.h> 
    #include <unistd.h> 
    #include <stdlib.h> 
    #include <pthread.h> 
    #include <mach/mach.h> 
    #include <mach/task.h> 
    #include <malloc/malloc.h> 
    #include <errno.h> 


    /** heapStats() 
    * print the output from the mstats() function: total heap bytes, 
    * used bytes, and free bytes. 
    */ 
    void heapStats() 
    { 
     struct mstats ms = mstats(); 
     malloc_printf(
     "total: %d, used: %d, free: %d\n", 
     ms.bytes_total, 
     ms.bytes_used, 
     ms.bytes_free); 
    } 

    /* heapInUse() 
    * Gather the heap usage metrics from each zone, using 
    * malloc_zone_statistics(). 
    */ 
    void heapInUse(
     size_t * bytesInUse, 
     size_t * blocksInUse, 
     size_t * sizeAllocated) 
    { 
     *bytesInUse = 0; 
     *blocksInUse = 0; 
     *sizeAllocated = 0; 
     unsigned int i; 
     vm_address_t * zones; 
     unsigned int count; 
     kern_return_t rc = 
     malloc_get_all_zones(mach_task_self(), 0, &zones, &count); 
     if (0 != rc) 
     { 
     fprintf(stderr, "rc was %d\n", rc); 
     } 
     for (i = 0; i < count; ++i) 
     { 
     malloc_zone_t * zone = (malloc_zone_t*)zones[i]; 
     char const * name = malloc_get_zone_name(zone); 
     if (NULL == name) 
     { 
      continue; 
     } 
     malloc_statistics_t stats; 
     stats.blocks_in_use = 0; 
     stats.size_in_use = 0; 
     stats.max_size_in_use = 0; 
     stats.size_allocated = 0; 
     malloc_zone_statistics(zone, &stats); 
     *bytesInUse += stats.size_in_use; 
     *blocksInUse += stats.blocks_in_use; 
     *sizeAllocated += stats.size_allocated; 
     } 
    } 

    /** main() 
    * entry point 
    */ 
    int main(int argc, const char * argv[]) 
    { 
     char * buff = (char *)0; 
     size_t bytesInUse = 0; 
     size_t blocksInUse = 0; 
     size_t sizeAllocated = 0; 
     printf("=== Initial conditions ===\n"); 
     heapInUse(&bytesInUse, &blocksInUse, &sizeAllocated); 
     printf(
     "in use: %zu, allocated: %zu, blocks: %zu\n", 
     bytesInUse, sizeAllocated, blocksInUse); 
     heapStats(); 

     printf("=== Before allocation ===\n"); 
     heapInUse(&bytesInUse, &blocksInUse, &sizeAllocated); 
     printf(
     "in use: %zu, allocated: %zu, blocks: %zu\n", 
     bytesInUse, sizeAllocated, blocksInUse); 
     heapStats(); 

     // Allocate the buffer 
     // 
     buff = (char *)malloc(10000); 
     printf("=== After malloc ===\n"); 
     heapInUse(&bytesInUse, &blocksInUse, &sizeAllocated); 
     printf(
     "in use: %zu, allocated: %zu, blocks: %zu\n", 
     bytesInUse, sizeAllocated, blocksInUse); 
     heapStats(); 

     // Free the buffer 
     // 
     if (NULL != buff) 
     { 
     free(buff); 
     buff = NULL; 
     } 
     printf("=== After free ===\n"); 
     heapInUse(&bytesInUse, &blocksInUse, &sizeAllocated); 
     printf(
     "in use: %zu, allocated: %zu, blocks: %zu\n", 
     bytesInUse, sizeAllocated, blocksInUse); 
     heapStats(); 

     // Get out 
     // 
     return 0; 
    } 

解決方案:感謝ŧ Ø從John Zwinck響應我接過仔細看看malloc/malloc.h,發現了一個方法malloc_zone_pressure_relief(),我可以使用強制堆釋放未使用的字節,這樣我可以得到準確的指標。

所以我加入這個方法:

void freeAsMuchAsPossible() 
{ 
    vm_address_t * zones; 
    unsigned int count; 
    unsigned int i; 

    kern_return_t rc = 
    malloc_get_all_zones(mach_task_self(), 0, &zones, &count); 
    if (0 != rc) 
    { 
    fprintf(stderr, "rc was %d\n", rc); 
    } 
    for (i = 0; i < count; ++i) 
    { 
    malloc_zone_t * zone = (malloc_zone_t*)zones[i]; 
    char const * name = malloc_get_zone_name(zone); 
    if (NULL == name) 
    { 
     continue; 
    } 
    malloc_zone_pressure_relief(zone, 0); 
    } 
} 

,並把它稱爲每次調用之前heapInUse(),像這樣:

printf("=== Before allocation ===\n"); 
freeAsMuchAsPossible(); 
heapInUse(&bytesInUse, &blocksInUse, &sizeAllocated); 
printf(
    "in use: %zu, allocated: %zu, blocks: %zu\n", 
    bytesInUse, sizeAllocated, blocksInUse); 
heapStats(); 

現在我得到預期的和有用的結果,例如:

=== Initial conditions === 
in use: 23584, allocated: 9437184, blocks: 4294966976 
SimpleLeaker(22142,0x7fff7b2a2310) malloc: total: 9437184, used: 23584, free: 9413600 
=== Before allocation === 
in use: 23584, allocated: 9437184, blocks: 4294966976 
SimpleLeaker(22142,0x7fff7b2a2310) malloc: total: 9437184, used: 23584, free: 9413600 
=== After malloc === 
in use: 33824, allocated: 9437184, blocks: 4294966967 
SimpleLeaker(22142,0x7fff7b2a2310) malloc: total: 9437184, used: 33824, free: 9403360 
=== After free === 
in use: 23584, allocated: 9437184, blocks: 4294966966 
SimpleLeaker(22142,0x7fff7b2a2310) malloc: total: 9437184, used: 23584, free: 9413600 

利用這種技術,我可以編寫單元測試,檢查內存泄漏。非常好。

回答

2

在OS X上,由於10.7,我們可以使用malloc_zone_pressure_relief()迫使堆釋放未使用的字節。我已經用解決方案更新了我的問題。

感謝John Zwinck對我的另一個細看malloc.h的反應,我發現了這個方法。

1

你可能是正確的:分配是每次調用空閒時間()不釋放內存返回給操作系統。但是你可以使用mallopt()來改變它。嘗試將M_MMAP_THRESHOLD設置爲0.這將使每個分配獨立。您可能不希望用於生產,但爲了測試它可能會對您有所幫助。

+1

'mallopt()'似乎是一個聰明的事情,並且將是在Linux系統上追求的一個很好的途徑。但是我正在使用OS X,'mallopt()'不可用,也許是因爲轉向了Clang。然而,這些建議讓我更加關注malloc.h,並且我發現了一個函數'malloc_zone_pressure_relief()',它似乎強制堆釋放未使用的字節。所以,謝謝你的推動。 – 2014-09-02 22:39:11

0

我會建議尋找到OSX leaks工具來查找內存泄漏的程序。介紹性文章可以在這裏找到:leaks documentation at Mac Developer Library。您可以通過man leaks瞭解更多詳情。另外,僅針對您的問題1,您可以使用libgmalloc。請man libgmalloc瞭解更多信息。另外,這些手冊頁包含指向其他工具的鏈接,例如malloc_history,您可能也會發現對於您的目的有用和/或更快。

+0

我想創建單元測試,發現內存泄漏,所以'泄漏'工具不會幫助我。但是當我需要排除通過自動化測試的泄漏問題時,知道它是可用的是很好的。我可以想象'malloc_history'也可能有用的情況。 – 2014-09-02 22:59:39