我的程序有一个自定义分配器,它使用mmap(MAP_ANON | MAP_PRIVATE)
从操作系统获取内存。当它不再需要内存时,分配器会调用munmap
或madvise(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;
}
答案 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页面的拒绝。