我Delphi程序的内存为什么一直增长?

2024-03-29 12:34:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用Delphi2009,它内置了FastMM4内存管理器。

我的程序读入并处理一个大数据集。每当我清除数据集或退出程序时,所有内存都会被正确释放。它根本没有内存泄漏。

使用spenwarr对How to get the Memory Used by a Delphi Program的回答中给出的currentmoreyusage例程,我显示了FastMM4在处理期间使用的内存。

似乎正在发生的是,在每一个进程和释放周期之后,内存的使用都在增长。e、 g.:

在没有数据集的情况下启动程序后使用了1456 KB。

加载大型数据集后使用的218455 KB。

完全清除数据集后为71994 KB。如果我在这一点(或我的示例中的任何一点)退出,则不会报告内存泄漏。

再次加载同一数据集后使用了271905 KB。

完全清除数据集后为125443 KB。

再次加载同一数据集后使用了325519 KB。

完全清除数据集后为179059 KB。

再次加载同一数据集后使用了378752 KB。

在每次加载/清除周期中,我的程序的内存使用量似乎增加了53400 KB。任务管理器确认这是实际发生的。

我听说FastMM4并不总是在释放对象时将程序的所有内存释放回操作系统,以便在需要更多内存时保留一些内存。但这种持续的增长让我烦恼。由于没有内存泄漏的报告,我无法识别问题。

有人知道为什么会发生这样的事情吗?如果事情不好,如果有什么我可以或者应该做的?


谢谢德索普和梅森的回答。你让我思考和尝试让我意识到我错过了什么。所以需要进行详细的调试。

事实证明,我的所有结构在退出时都得到了适当的释放。但在运行过程中,每次循环后的内存释放没有。它正在累积内存块,如果我的出口清理不正确的话,通常会导致在出口时可以检测到的泄漏——但事实上确实如此。

我需要在循环之间清除一些字符串列表和其他结构。我仍然不确定我的程序是如何正确地工作的,从早期的周期中仍然有额外的数据,但它确实做到了。我可能会进一步研究。

这个问题已经回答了。谢谢你的帮助。


Tags: to数据内存程序管理器kb报告事情
3条回答

你在用什么样的数据集?如果它完全在Delphi中实现(而不是使用另一个内存管理器(如Midas)调用其他代码),则可以尝试故意泄漏数据集。

我假设您的数据集在表单上,当表单清除其组件时,它会被自动释放。尝试将MyDataset := nil;放入表单的OnDestroy中。这将确保数据集泄漏,并且还将数据集拥有的所有内容。在加载两次之后,尝试一次又一次地加载,并比较泄漏报告,看看是否有什么有用的东西。

很明显,你在半漏记忆。当程序运行时,您正在泄漏内存,但是当您关闭程序时,您的数据集被正确释放,因此FastMM(正确地)不会报告它。

有关详细信息,请参见:My program never releases the memory back. Why?

链接到的CurrentMemoryUsage实用程序报告应用程序的工作集大小。Working set是映射到物理内存地址的虚拟内存地址空间的总页数。然而,这些页面中的一些或许多页面中存储的实际数据可能非常少。因此,工作集是进程正在使用的内存量的“上限”。它指示为使用保留了多少地址空间,但不指示实际提交了多少(实际驻留在物理内存中)或提交的页面中有多少实际被应用程序使用。

尝试以下操作:在您看到工作集大小在几次测试运行后逐渐增大后,请最小化应用程序的主窗口。您很可能会看到工作集大小显著下降。为什么?因为当最小化丢弃未使用页并将工作集缩小到最小值的应用程序时,Windows会执行SetProcessWorkingSetSize(-1)调用。当应用程序窗口大小正常时,操作系统不会这样做,因为过于频繁地减小工作集大小会迫使数据从交换文件重新加载,从而使性能变差。

更详细地说:您的Delphi应用程序将内存分配成相当小的块-这里是一个字符串,那里是一个类。程序的平均内存分配通常少于几百字节。很难在系统范围内有效地管理像这样的小分配,所以操作系统不会。它可以有效地管理大内存块,特别是在4k虚拟内存页大小和64k虚拟内存地址范围最小大小的情况下。

这给应用程序带来了一个问题:应用程序通常分配小的块,但是操作系统将内存分配成相当大的块。怎么办?答案:不可分配。

Delphi运行时库的内存管理器和FastMM替换内存管理器(以及这个星球上几乎所有其他语言或工具集的运行时库)都可以做一件事:将操作系统中的大内存块分割成应用程序使用的小内存块。跟踪所有的小模块在哪里,它们有多大,以及它们是否被“泄露”需要一些内存和所谓的开销。

在大量内存分配/释放的情况下,可能会有这样的情况,即释放99%的分配,但进程的工作集大小只缩小50%。为什么?最常见的情况是,这是由堆碎片造成的:Delphi内存管理器从操作系统获得并在内部划分的一个大块中,仍有一小块内存在使用。使用的内存的内部计数很小(比如说,300字节),但是由于它阻止堆管理器释放它在操作系统中的大块,所以这个300字节的小块的工作集贡献更像是4k(或者64k,取决于它是虚拟页还是虚拟地址空间——我想不起来)。

在涉及兆字节小内存分配的大量内存密集型操作中,堆碎片非常常见—特别是当与内存密集型操作无关的内容的内存分配与大作业同时进行时。例如,如果在处理80MB数据库操作的过程中还将状态输出到列表框,则用于报告状态的字符串将分散在数据库内存块的堆中。当释放数据库计算使用的所有内存块时,listbox字符串仍然在那里(使用中,没有丢失),但它们分散在各个地方,可能会为每个小字符串占用整个OS大块。

尝试最小化窗口技巧,看看这是否会减少您的工作集。如果是,您可以对工作集计数器返回的数字的明显“严重性”打折扣。你也可以给etProcessWorkingSetSize在执行大计算操作后清除不再使用的页面。

相关问题 更多 >