我在使用WCF web服务时遇到了一些问题(一些转储、内存泄漏等)。我运行了一个有用的工具(ANTS Memory Profiles)。
只是为了找出,即使处理结束(我运行了一个特定的测试,然后停止),第2代仍然是web服务内存的25%。我跟踪了这个内存,发现我有一个充满(null,null)项的字典对象,带有-1哈希码。
web服务的工作流意味着,在特定的处理过程中,会添加项目,然后从字典中删除这些项目(只需简单的Add
和Remove
)。没什么大不了的。但似乎在删除所有项后,字典中充满了(null,null) KeyValuePair
。实际上,它们占据了很大一部分内存,最终发生溢出,相应的强制应用程序池回收和DW20.exe获得了它可以获得的所有CPU周期。
字典实际上是Dictionary<SomeKeyType, IEnumerable<KeyValuePair<SomeOtherKeyType, SomeCustomType>>>
(System.OutOfMemoryException because of Large Dictionary),所以我已经检查了是否有某种引用保存了一些东西。
字典包含在一个静态对象中(通过处理使它可以被不同的处理线程访问),所以从这个问题和更多的问题(Do static members ever get garbage collected?)中,我理解了为什么字典在第二代。但这也是导致这些(null,null)的原因?即使我从字典中删除条目,内存中也总会有东西被占用吗?
这不是像这个问题Deallocate memory from large data structures in C#中那样的速度问题。似乎内存永远不会被回收。
我可以做些什么来从字典中实际删除条目,而不是仅仅用(null,null)对填充它?还有什么我需要结帐的吗?
发布于 2013-02-22 12:26:12
字典将条目存储在哈希表中。对此,内部使用了一个数组。由于哈希表的工作方式,此数组必须始终大于实际存储的项数(至少大30% )。微软使用72%的负载因子,即至少28%的数组将是空的(请参阅An Extensive Examination of Data Structures Using C# 2.0,特别是The System.Collections.Hashtable Class和The System.Collections.Generic.Dictionary Class),因此null/null条目只能表示此可用空间。
如果数组太小,它将自动增长;但是,当项被移除时,数组不会缩小,但在插入新项时,应重用将释放的空间。
如果您可以控制此字典,您可以尝试重新创建它,以缩小它:
theDict = new Dictionary<TKey, IEnumerable<KeyValuePair<TKey2, TVal>>>(theDict);
但问题可能来自实际的(非空)条目。您的字典是静态的,因此垃圾收集器永远不会自动回收它,除非您为它分配另一个字典或null
(theDict = new ...
或theDict = null
)。这只适用于字典本身,它是静态的,而不是它的条目。只要其他地方存在对已删除条目的引用,它们就会持续存在。GC将回收任何不能再通过引用访问的对象(更早或更晚)。无论这个对象是否被声明为静态,都没有区别。对象本身不是静态的,只是它们的引用。
正如@RobertTausig友好地指出的那样,自从.NET Core2.1以来就有了新的Dictionary.TrimExcess()
,这是你真正想要的,但当时还不存在。
发布于 2013-02-22 12:08:23
看起来你需要定期回收字典中的空间。您可以通过创建一个新的new Dictionary<a,b>(oldDict)
来做到这一点。请确保以线程安全的方式执行此操作。
何时执行此操作?无论是在计时器的滴答声中(60秒?)或者发生特定数量的写入时(100k?)(你需要保留一个修改计数器)。
发布于 2013-08-27 10:23:26
一种解决方案是在静态字典上调用Clear()方法。这样,对字典的引用将保持可用,但包含的对象将被释放。
https://stackoverflow.com/questions/15022528
复制