<p>链接到的CurrentMemoryUsage实用程序报告应用程序的工作集大小。Working set是映射到物理内存地址的虚拟内存地址空间的总页数。然而,这些页面中的一些或许多页面中存储的实际数据可能非常少。因此,工作集是进程正在使用的内存量的“上限”。它指示为使用保留了多少地址空间,但不指示实际提交了多少(实际驻留在物理内存中)或提交的页面中有多少实际被应用程序使用。</p>
<p>尝试以下操作:在您看到工作集大小在几次测试运行后逐渐增大后,请最小化应用程序的主窗口。您很可能会看到工作集大小显著下降。为什么?因为当最小化丢弃未使用页并将工作集缩小到最小值的应用程序时,Windows会执行SetProcessWorkingSetSize(-1)调用。当应用程序窗口大小正常时,操作系统不会这样做,因为过于频繁地减小工作集大小会迫使数据从交换文件重新加载,从而使性能变差。</p>
<p>更详细地说:您的Delphi应用程序将内存分配成相当小的块-这里是一个字符串,那里是一个类。程序的平均内存分配通常少于几百字节。很难在系统范围内有效地管理像这样的小分配,所以操作系统不会。它可以有效地管理大内存块,特别是在4k虚拟内存页大小和64k虚拟内存地址范围最小大小的情况下。</p>
<p>这给应用程序带来了一个问题:应用程序通常分配小的块,但是操作系统将内存分配成相当大的块。怎么办?答案:不可分配。</p>
<p>Delphi运行时库的内存管理器和FastMM替换内存管理器(以及这个星球上几乎所有其他语言或工具集的运行时库)都可以做一件事:将操作系统中的大内存块分割成应用程序使用的小内存块。跟踪所有的小模块在哪里,它们有多大,以及它们是否被“泄露”需要一些内存和所谓的开销。</p>
<p>在大量内存分配/释放的情况下,可能会有这样的情况,即释放99%的分配,但进程的工作集大小只缩小50%。为什么?最常见的情况是,这是由堆碎片造成的:Delphi内存管理器从操作系统获得并在内部划分的一个大块中,仍有一小块内存在使用。使用的内存的内部计数很小(比如说,300字节),但是由于它阻止堆管理器释放它在操作系统中的大块,所以这个300字节的小块的工作集贡献更像是4k(或者64k,取决于它是虚拟页还是虚拟地址空间——我想不起来)。</p>
<p>在涉及兆字节小内存分配的大量内存密集型操作中,堆碎片非常常见—特别是当与内存密集型操作无关的内容的内存分配与大作业同时进行时。例如,如果在处理80MB数据库操作的过程中还将状态输出到列表框,则用于报告状态的字符串将分散在数据库内存块的堆中。当释放数据库计算使用的所有内存块时,listbox字符串仍然在那里(使用中,没有丢失),但它们分散在各个地方,可能会为每个小字符串占用整个OS大块。</p>
<p>尝试最小化窗口技巧,看看这是否会减少您的工作集。如果是,您可以对工作集计数器返回的数字的明显“严重性”打折扣。你也可以给etProcessWorkingSetSize在执行大计算操作后清除不再使用的页面。</p>