如何强制MacOS释放MADV_FREE页面?

时间:2011-10-10 21:34:03

标签: macos memory-management

我的程序有一个自定义分配器,它使用mmap(MAP_ANON | MAP_PRIVATE)从操作系统获取内存。当它不再需要内存时,分配器会调用munmapmadvise(MADV_FREE)MADV_FREE保持映射,但告诉操作系统它可以丢弃与映射关联的物理页面。

在您最不需要的网页上调用MADV_FREE最终比调用munmap并稍后再次调用mmap要快得多。

这对我来说几乎是完美的。唯一的问题是,在MacOS上,MADV_FREE对于摆脱我要求它释放的页面非常懒惰。事实上,只有当来自另一个应用程序的内存压力时,它才会消除它们。在它摆脱我释放的页面之前,MacOS报告我的程序仍在使用该内存;在活动监视器中,其“实内存”列不反映释放的内存。

这让我很难测量我的程序实际使用的内存量。 (测量RSS的这种困难使我们无法在10.5上登陆自定义分配器。)

我可以分配一大堆内存来强制操作系统释放这些页面,但除了花费很长时间之外,还可能产生其他副作用,例如导致部分程序被分页到磁盘。

在云雀上,我尝试了purge命令,但这没有效果。

如何强制MacOS清除这些MADV_FREE页面?或者,我如何向MacOS询问我的进程在内存中有多少个MADV_FREE页?

这是一个测试程序,如果有帮助的话。程序进入休眠状态后,Activity Monitor的“Real Memory”列显示512MB。在我的Linux机器上,top根据需要显示了256MB的RSS。

#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>

#define SIZE (512 * 1024 * 1024)

// We use MADV_FREE on Mac and MADV_DONTNEED on Linux.
#ifndef MADV_FREE
#define MADV_FREE MADV_DONTNEED
#endif

int main()
{
  char *x = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);

  // Touch each page we mmap'ed so it gets a physical page.
  int i;
  for (i = 0; i < SIZE; i += 1024) {
    x[i] = i;
  }

  madvise(x, SIZE / 2, MADV_FREE);

  fprintf(stderr, "Sleeping.  Now check my RSS.  Hopefully it's %dMB.\n", SIZE / (2 * 1024 * 1024));
  sleep(1024);
  return 0;
}

3 个答案:

答案 0 :(得分:3)

mprotect(addr, length, PROT_NONE);
mprotect(addr, length, PROT_READ | PROT_WRITE);

请注意,正如你所说,madvise更为懒散,这可能对性能更好(以防万一有人试图将其用于性能而不是测量)。

答案 1 :(得分:1)

在macOS上使用MADV_FREE_REUSABLE。根据Apple的magazine_malloc实现:

  

在OS X上,我们使用MADV_FREE_REUSABLE,它向内核发出信号以从进程的内存统计信息中删除给定的页面。但是,在返回该内存以使用时,我们必须发出信号,告知其已被重用。

https://opensource.apple.com/source/libmalloc/libmalloc-53.1.1/src/magazine_malloc.c.auto.html

例如,

铬也使用它:

  

MADV_FREE_REUSABLE与MADV_FREE相似,但还使用可重用位标记页面,这使“活动监视器”和内存基础设施都可以正确跟踪页面。

https://github.com/chromium/chromium/blob/master/base/memory/discardable_shared_memory.cc#L377

答案 2 :(得分:0)

我看了看,我觉得这不可能。 :\

我们通过向分配器添加代码来解决问题,该分配器在我们要求时明确地解除对MADV_FREE页面的拒绝。