为什么mstats和malloc_zone_statistics释放后不显示恢复的内存? [英] Why does mstats and malloc_zone_statistics not show recovered memory after free?

查看:95
本文介绍了为什么mstats和malloc_zone_statistics释放后不显示恢复的内存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一些单元测试,以检查OS X(10.9 Mavericks)上是否已释放内存(检查内存泄漏).我正在尝试使用mstats()和malloc_zone_statistics()发现我需要的信息.但是似乎它们没有显示内存已释放(请参见下面的示例输出...调用free()后内存使用率不会改变)

I'm trying to write some unit tests that check whether memory has been freed -- to check for memory leaks -- on OS X (10.9 Mavericks). I'm trying to use mstats() and malloc_zone_statistics() to discover the information I need. But it seems that they don't show memory being released (see example output below ... memory usage does not change after free() is called)

我怀疑这与堆管理有关,而不是与那些功能有关.我认为堆不会释放已释放的内存,也许这样它就可以重用它而无需删除和添加块的开销.

I suspect this has more to do with the heap management than with a problem with those functions. I think the heap is not releasing freed memory, perhaps so it can reuse it without the overhead of removing and adding blocks.

  1. 是否有某种方法告诉堆释放已释放的块?要更进取还是关闭优化?
  2. 我只是错误地使用了mstats()或malloc_zone_statistics()吗?

更新:找到解决方案...位于底部...

Update: solution found ... provided at the bottom ...

这是我的测试程序的输出:

Here is the output from my test program:

=== 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(),我可以用它强制堆释放未使用的字节,以便获得准确的指标.

Solution: Thanks to the response from John Zwinck I took a closer look at malloc/malloc.h and found a method malloc_zone_pressure_relief() that I can use to force the heap to free unused bytes so that I can get accurate metrics.

所以我添加了此方法:

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()之前调用它,就像这样:

and called it before every call to heapInUse(), like so:

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

现在,我得到了预期的有用结果,例如:

And now I get expected and useful results, for example:

=== 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

使用这种技术,我可以编写检查内存泄漏的单元测试.很好.

With this technique I can write unit tests that check for memory leaks. Very nice.

推荐答案

在OS X上,自10.7开始,我们可以使用malloc_zone_pressure_relief()强制堆释放未使用的字节.我已经用解决方案更新了我的问题.

On OS X, since 10.7, we can use malloc_zone_pressure_relief() to force the heap to release unused bytes. I've updated my question with the solution.

感谢 John Zwinck 的回复,这促使我对malloc.h进行了仔细的研究.该方法.

Thanks to the response from John Zwinck for prodding me into another close look at malloc.h where I found that method.

这篇关于为什么mstats和malloc_zone_statistics释放后不显示恢复的内存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆