为什么我的Delphi程序的内存会继续增长?

lke*_*ler 12 delphi memory-management fastmm

我正在使用Delphi 2009,它内置了FastMM4内存管理器.

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

使用spenwarr答案中给出的CurrentMemoryUsage例程:如何获取Delphi程序使用的内存,我已经显示了FastMM4在处理过程中使用的内存.

似乎正在发生的事情是,在每个进程和发布周期之后,内存的使用正在增长.例如:

在没有数据集的情况下启动程序后使用1,456 KB.

加载大型数据集后使用218,455 KB.

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

再次加载相同的数据集后使用271,905 KB.

完全清除数据集后为125,443 KB.

再次加载相同的数据集后使用325,519 KB.

完全清除数据集后179,059 KB.

再次加载相同的数据集后使用378,752 KB.

在每个加载/清除周期中,我的程序的内存使用量似乎增加了大约53,400 KB.任务管理器确认这确实发生了.

我听说当释放对象时,FastMM4并不总是将所有程序的内存释放回操作系统,以便在需要更多时可以保留一些内存.但这种不断增长使我感到困扰.由于没有报告内存泄漏,我无法确定问题.

有谁知道为什么会这样,如果它是坏的,如果有什么我可以或应该做些什么呢?


谢谢dthorpe和Mason的回答.你让我思考并尝试让我意识到自己错过了什么的事情.因此需要进行详细的调试.

事实证明,我的所有结构在退出时都被正确释放.但是在运行期间每个循环后的内存释放不是.它正在累积内存块,如果我的退出清理不正确,通常会导致泄漏,如果我的退出清理不正确则会在退出时检测到 - 但事实确实如此.

我需要在循环之间清除一些StringLists和其他结构.我仍然不确定我的程序如何正常工作,还有早期周期中的额外数据,但确实如此.我可能会进一步研究.

这个问题已得到解答.谢谢你的帮助.

dth*_*rpe 25

您链接的CurrentMemoryUsage实用程序报告您的应用程序的工作集大小.工作集是映射到物理内存地址的虚拟内存地址空间的总页数.但是,这些页面中的一些或许多页面可能存储的实际数据非常少.因此,工作集是您的进程使用多少内存的"上限".它指示保留多少地址空间以供使用,但它不表示实际提交了多少(实际驻留在物理内存中)或者应用程序实际使用了多少提交的页面.

试试这个:在看到你的工作集大小在几次测试运行后爬升之后,最小化你的应用程序的主窗口.您很可能会看到工作集大小显着下降.为什么?因为当您最小化丢弃未使用页面并将工作集缩小到最小的应用程序时,Windows会执行SetProcessWorkingSetSize(-1)调用.当应用程序窗口的大小正常时,操作系统不会执行此操作,因为过于频繁地减小工作集大小会因强制从交换文件重新加载数据而使性能变差.

更详细地介绍它:你的Delphi应用程序以相当小的块分配内存 - 这里是一个字符串,那里是一个类.程序的平均内存分配通常小于几百字节.在系统范围内有效管理这样的小分配很困难,因此操作系统不能.它有效地管理大内存块,特别是在4k虚拟内存页面大小和64k虚拟内存地址范围最小大小.

这给应用程序带来了一个问题:应用程序通常会分配小块,但是OS会以相当大的块来分配内存.该怎么办?答案:suballocate.

Delphi运行时库的内存管理器和FastMM替换内存管理器(以及地球上几乎所有其他语言或工具集的运行时库)都可以做一件事:将操作系统中的大内存块划分为更小的块.应用.跟踪所有小块的位置,它们有多大,以及它们是否"泄露"都需要一些内存 - 称为开销.

在大量内存分配/释放的情况下,可能会出现释放99%所分配内容的情况,但进程的工作集大小仅缩小50%.为什么?大多数情况下,这是由堆碎片引起的:Delphi内存管理器从操作系统获取并在内部分配的一个大块中仍然使用了一小块内存.使用的内存计数很小(比方说300字节),但由于它阻止了堆管理器释放它回到操作系统的大块,因此该300英里小块的工作集贡献更像是4k(或64k取决于它是虚拟页面还是虚拟地址空间 - 我不记得了.

在涉及兆字节小内存分配的大量内存密集型操作中,堆碎片非常常见 - 特别是如果与内存密集型操作无关的内容的内存分配与大型作业同时进行.例如,如果通过80MB数据库操作进行运算,也会随着进度的推移将状态输出到列表框,则用于报告状态的字符串将分散在数据库内存块中的堆中.当您释放数据库计算使用的所有内存块时,列表框字符串仍在那里(在使用中,不会丢失)但它们遍布整个地方,可能占用每个小字符串的整个OS大块.

尝试最小化窗口技巧,看看是否会减少您的工作集.如果是,您可以折扣工作集计数器返回的数字的明显"严重性".您还可以在大型计算操作之后添加对SetProcessWorkingSetSize的调用,以清除不再使用的页面.

  • 是的,我知道FastMM的设计.我帮助批评它的实现并将其纳入Delphi产品.FastMM仍然容易受到夸大的工作集大小的影响. (5认同)