我正在尝试编写一些单元测试来检查内存是否已被释放 - 以检查内存泄漏 - 在OS X(10.9 Mavericks)上。我正在尝试使用mstats()和malloc_zone_statistics()来发现我需要的信息。但似乎他们没有显示内存被释放(参见下面的示例输出......在调用free()后内存使用率不会改变)
我怀疑这与堆管理有关,而不是与这些功能有问题。我认为堆不会释放释放的内存,也许它可以重用它而不需要删除和添加块的开销。
更新:找到的解决方案......在底部提供......
以下是我的测试程序的输出:
=== 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
使用这种技术,我可以编写检查内存泄漏的单元测试。很好。
答案 0 :(得分:2)
在OS X上,从10.7开始,我们可以使用malloc_zone_pressure_relief()
强制堆释放未使用的字节。我已用解决方案更新了我的问题。
感谢John Zwinck的回复,促使我再次密切关注malloc.h,在那里我发现了这种方法。
答案 1 :(得分:1)
你可能是对的:每次调用free()时,分配器都不会将内存释放回操作系统。但您可以使用mallopt()更改此设置。尝试将M_MMAP_THRESHOLD设置为0.这将使每个单独的分配独立。您可能不希望将其用于生产,但是对于测试它可能会对您有所帮助。
答案 2 :(得分:0)
我建议您查看OSX leaks
工具以查找程序中的内存泄漏。可在此处找到介绍性文章:leaks documentation at Mac Developer Library。您可以man leaks
了解更多详情。另外,仅针对您的问题1,您可以使用libgmalloc
。请man libgmalloc
了解详情。此外,这些手册页还包含指向malloc_history
等其他工具的链接,您可能会发现这些工具对您的目的有用和/或更快。