17

我在使用 WCF Web 服务时遇到了一些问题(一些转储、内存泄漏等),我运行了一个 profillng 工具(ANTS 内存配置文件)。

只是为了发现即使处理结束(我运行特定测试然后停止),第 2 代也是 Web 服务内存的 25%。我追踪了这段内存,发现我有一个字典对象,里面满是 (null, null) 项,哈希码为 -1。

Web 服务的工作流意味着在特定的处理过程中添加项目,然后从字典中删除(只是简单的Addand Remove)。没有大碍。但似乎在删除所有项目后,字典中充满了 (null, null) KeyValuePair。实际上有数千个,这样它们占用了很大一部分内存并最终发生溢出,相应的强制应用程序池回收和 DW20.exe 获得了它可以获得的所有 CPU 周期。

字典实际上是Dictionary<SomeKeyType, IEnumerable<KeyValuePair<SomeOtherKeyType, SomeCustomType>>>System.OutOfMemoryException 因为大字典)所以我已经检查了是否有某种引用保存的东西。

字典包含在一个静态对象中(以使其可以通过处理访问不同的处理线程),所以从这个问题以及更多(静态成员是否得到垃圾收集?)我理解为什么该字典在第 2 代。但这是还有那些(null,null)的原因?即使我从字典中删除项目,内存中总是会占用一些东西?

这不是一个速度问题,就像这个问题Deallocate memory from large data structures in C#。似乎记忆永远不会被回收。

我可以做些什么来实际从字典中删除项目,而不仅仅是继续用 (null, null) 对填充它?还有什么我需要检查的吗?

4

3 回答 3

16

字典将项目存储在哈希表中。为此在内部使用了一个数组。由于哈希表的工作方式,该数组必须始终大于实际存储的项目数(至少大 30% 左右)。Microsoft 使用 72% 的负载因子,即至少 28% 的数组将为空(请参阅使用 C# 2.0 对数据结构进行广泛检查,尤其是System.Collections.Hashtable 类System.Collections.Generic.Dictionary 类) 因此,null/null 条目可能只代表这个可用空间。

如果数组太小,会自动增长;但是,当删除项目时,数组不会缩小,但是在插入新项目时应该重新使用将被释放的空间。

如果您可以控制此字典,则可以尝试重新创建它以缩小它:

theDict = new Dictionary<TKey, IEnumerable<KeyValuePair<TKey2, TVal>>>(theDict);

但问题可能来自实际(非空)条目。您的字典是静态的,因此垃圾收集器永远不会自动回收,除非您为其分配另一个字典或null(theDict = new ...theDict = null)。这仅适用于静态的字典本身,而不适用于其条目。只要对已删除条目的引用存在于其他地方,它们就会持续存在。GC 将回收任何无法通过某些引用再访问的对象(较早或较晚)。不管这个对象是否被声明为静态的,都没有区别。对象本身不是静态的,只有它们的引用。


正如@RobertTausig 好心指出的那样,由于 .NET Core 2.1有了 new Dictionary.TrimExcess(),这是您真正想要的,但当时并不存在。

于 2013-02-22T12:26:12.393 回答
5

看起来您需要定期回收该字典中的空间。您可以通过创建一个新的来做到这一点:new Dictionary<a,b>(oldDict). 请务必以线程安全的方式执行此操作。

什么时候做这个?无论是在计时器的滴答声(60 秒?)还是发生特定数量的写入(100k?)时(您需要保留一个修改计数器)。

于 2013-02-22T12:08:23.397 回答
0

一个解决方案可能是在静态字典上调用Clear () 方法。这样,对字典的引用将保持可用,但包含的对象将被释放。

于 2013-08-27T10:23:25.670 回答