我的程序读入并处理大型数据集.每当我清除数据集或退出程序时,所有内存都将被正确释放.它根本没有内存泄漏.
使用spenwarr的答案中给出的CurrentMemoryUsage例程:How to get the Memory Used by a Delphi Program,我在处理过程中显示了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和梅森的答案.你让我思考和尝试让我意识到的东西我失去了一些东西.所以需要详细的调试.
事实证明,我的所有结构都在出口时被正确地释放.但在运行中的每个周期之后的内存释放不是.它正在累积的内存块,通常会导致泄漏,如果我的出口清理是不正确的,可以在出口检测到 – 但它是.
有一些StringLists和其他结构我需要清除循环之间.我仍然不知道我的程序如何正常工作,与之前的周期仍然存在的额外数据,但它确实如此.我可能会进一步研究.
这个问题已经回答了.谢谢你的帮助.
解决方法
尝试这样:在几次测试运行后,看到您的工作集大小爬升后,最小化应用程序的主窗口.你很可能会看到工作集的大小明显下降.为什么?因为Windows最小化丢弃未使用的页面并将工作集缩小到最小的应用程序时,Windows会执行SetProcessWorkingSetSize(-1)调用.当应用程序窗口正常大小时,操作系统不会执行此操作,因为通常会通过强制将数据从交换文件重新加载来降低工作集大小,从而使性能更糟.
要更详细地了解:您的Delphi应用程序分配内存相当小的块 – 一个字符串在这里,一个类在那里.程序的平均内存分配通常小于几百字节.在系统范围内很难高效地管理这样的小分配,所以操作系统不会.它有效地管理大型内存块,特别是在4k虚拟内存页面大小和64k虚拟内存地址范围的最小尺寸.
这为应用程序提出了一个问题:应用程序通常会分配小块,但是操作系统会在相当大的块中释放内存.该怎么办?答案:suballocate.
Delphi运行时库的内存管理器和FastMM替换内存管理器(以及行星库中的所有其他语言或工具集)都存在这样的一件事情:将大的内存块从操作系统中删除到应用.跟踪所有小块的位置,它们有多大,以及它们是否被“泄露”都需要一些内存,也就是开销.
在内存分配/重新分配的情况下,可能会释放您分配的99%的分配情况,但流程的工作集大小仅缩小50%.为什么?大多数情况下,这是由堆碎片引起的:Delphi内存管理器从操作系统中获取并在内部分派的一个大块中仍然使用一小块内存.使用的内存内存计数很小(说明是300字节),但是由于它阻止堆管理器释放它回到操作系统的大块,所以这个小300字节块的工作集贡献更像是4k(或64k取决于它是虚拟页面还是虚拟地址空间 – 我不记得了).
在涉及兆字节小内存分配的大量内存密集型操作中,堆碎片非常普遍 – 特别是如果与内存密集型操作无关的内存分配与大型工作同时进行.例如,如果通过80MB数据库操作进行操作,则会在状态进行时向列表框输出状态,用于报告状态的字符串将分散在数据库内存块中的堆中.当释放数据库计算使用的所有内存块时,列表框的字符串仍然在那里(在使用中,不会丢失),但它们分散在整个地方,可能占用每个小字符串的整个OS大块.
尝试最小化窗口技巧来查看是否减少了您的工作集.如果这样做,您可以折扣工作集计数器返回的数字的明显“严重性”.您也可以在大型计算操作之后添加对SetProcessWorkingSetSize的调用,以清除不再使用的页面.