C/Pascal 的堆管理器,自动用零字节填充释放的内存



您如何看待用零字节填充释放(未实际使用)页面的选项?这可能会提高 Windows 以及 VMWare 和其他虚拟机环境下的性能?例如,VMWare 和 HyperV 计算内存页的哈希,如果内容相同,则将此页标记为虚拟机内部和同一主机上的虚拟机之间的"共享",直到修改该页。它有效地减少了内存消耗。Windows做同样的事情 - 它以不同的方式处理零页,将它们视为免费页面。

我们可以有一个堆管理器,当我们调用FreeMem/ReallocMem时,它会自动用零填充内存。作为替代选择,我们可以有一个函数,按需求将空内存归零,即仅当显式调用此函数时。当然,此函数必须是线程安全的。 用零填充内存的缺点是接触内存,内存可能已经变成虚拟内存,从而发出页面错误。除此之外,任何内存存储操作都很慢,因此我们的程序会变慢,尽管程度未知(可能可以忽略不计)。

如果我们设法用零完全填充 4-K 页面,虚拟机管理程序或 Windows 会明确将其标记为零页面。但即使是部分清零也可能是有益的,因为虚拟机管理程序可能会使用 LZ 或类似算法压缩页面以节省物理内存。

我只想知道您的意见,堆管理器本身用零字节填充清空的堆内存的好处是否会超过这种技术的缺点。

当我们购买减少的物理内存消耗时,归零是否值得?

当您有一个页面的内容不再关心,但您仍然希望将其分配时,您可以调用VirtualAlloc(和变体)并传递MEM_RESET标志。

从 MSDN 上的VirtualAlloc

MEM_RESET

指示 lpAddress 指定的内存范围内的数据和 dwSize不再感兴趣。不应从或 写入分页文件。但是,将使用内存块 稍后再说,所以它不应该被取消。此值不能 与任何其他值一起使用。

使用此值并不能保证 使用 MEM_RESET 操作的范围将包含零。如果需要帮助, 包含零的范围,取消提交内存,然后重新提交。

这提供了两全其美的效果 - 您没有将内存归零的成本,并且系统没有将其分页的成本。 您可以利用已经具有零池的经过良好调整的内存管理器。

类似的功能也存在于 Linux 上,在MADV_FREE(或 Posix 的MADV_DONTNEED)标志下指向madvise。 Glibc 在其堆的实现中使用此函数:

/* 
* Stack:
* int shrink_heap (heap_info *h, long diff)
* int heap_trim (heap_info *heap, size_t pad) at arena.c:660
* void _int_free (mstate av, mchunkptr p, int have_lock) at malloc.c:4097
* void __libc_free (void *mem) at malloc.c:2948
* void free(void *mem)
*/
static int
shrink_heap (heap_info *h, long diff)
{
long new_size;
new_size = (long) h->size - diff;
/* ... snip ...  */
__madvise ((char *) h + new_size, diff, MADV_DONTNEED);
/* ... snip ...  */
h->size = new_size;
return 0;
}

如果您的堆在用户空间中,这将永远不起作用。内核只能信任自己,不能信任用户空间。如果内核将页面归零,它可以将其视为零。如果用户空间说它已将页面归零,内核仍然需要检查。它也可以将其归零。用户空间可以做的一件事是丢弃页面。这标志着他们"不在乎"。然后内核可以将它们视为零。但是手动将用户空间中的页面归零是徒劳的。

最新更新