大型字典导致System.OutOfMemoryException异常

6

我在一个字典中保存了一个大缓存,值为IEnumerable<KeyValuePair<DateTime,Double>>。 我定期从字典中删除项目并定期向字典添加项目。 不时地,我会遇到System.OutOfMemoryException异常。 我想知道为什么垃圾收集器不来帮助我?


1
你的缓存中保存了多少项?1000?1000000?我可以看到,也许大对象堆会变得碎片化,而在某个点之后你将无法扩展字典。 - Gabe
你应该意识到,存储在字典中的项目不会被垃圾回收。至少在它们从字典中移除并释放任何其他引用之前,它们不会被回收。 - Josh Smeaton
我建议运行内存分析器以查看哪些对象被保留在内存中。或者获取崩溃转储并检查堆使用情况。 - Chansik Im
为什么垃圾回收器没有及时出现帮助我呢?GC并不能阻止你自我扼杀。 - MattDavey
4个回答

3
请注意,如@Gabe所述,堆可以被分段。 即使您可能有空闲的内存,当它执行调整大小时,可能没有足够大的块可用于分配字典。
也许您可以使用来自模式和实践库的缓存块MSDN Library链接来帮助您实现良好的缓存。 也许您可以选择不动态分配内存、具有固定条目数的算法?
还要注意,如果没有可用的内存,则问题出在您的缓存大小上,而不是垃圾收集器。

2
很可能GC已经在大部分时间内帮助你了,但有时候你超出了它的能力范围。
为了明确起见,这是一个Dictionary还是一个Dictionary>>? 如果是后者,那么也许你在其他地方仍然持有引用?
你的缓存有多大?你有监控来跟踪吗?是什么让你认为是字典导致了问题?如果你可以控制缓存的大小,你尝试过减小大小吗?

我有一个包含500个元素的字典,每个元素都有一个SomeKeyType键,IEnumerable<KeyValuePair<DateTime,Double>>中每个元素大约有5000个键值对。因此,大约有2,500,000个KVPs,即5000 * 500。据我所知,DateTime占用8个字节,Double占用8个字节,每个元素大约16个字节,因此大约40兆字节。我不明白为什么会有问题。我倾向于在添加新元素之前从字典中删除元素。 - Anish Patel
@Anish:好吧,你还没有说你正在使用哪个IEnumerable<T>的实现。例如,如果你正在使用一个LinkedList,那么每个元素都会有相当大的开销。即使这样,我也不会认为250万个KVP会成为问题。是什么让你相信是这个缓存引起了问题? - Jon Skeet

2

既然您问为什么垃圾回收器不会拯救您,那我就给出一个答案。

使用带有垃圾回收器的编程语言/环境确实可以让您的生活更轻松,但并不意味着内存管理已经成为过去式。

如果在32位xp机器上分配超过2G的大块内存,则刚刚达到了.NET内存边界之一。保留2G的内存总是一个坏主意。

在内存受限的机器上运行巨大的数据库或类似的东西,您很快就会达到可用内存的边界。由于GC不知道操作系统的情况,所以它可能无法及时注意到低内存情况(创建像位图这样的巨大对象可能会触发此情况)。一旦将巨大对象设置为null,手动调用GC.Collect可以在这里提供很大的帮助。

在内存中保留一个大字典只是一个非常简单的描述。您能告诉我们集合中有哪些内容,以及理论上这些项目有多大吗?

如果您的意思是具有2,147,483,647个项目的大字典,则可能已经达到了整数大小限制。

总之:

  • 不要在内存中保留不需要的项目或将它们交换到磁盘上。
  • 一旦释放了“大”项目,请调用GC.Collect(但请勿在删除项目的循环中调用,而是在循环之后)

哦,还可以使用性能计数器来测量应用程序使用的内存量,这将告诉你发生了什么。 - CodingBarfield

0

我不确定,如果我错了,请原谅,但也许当字典大于85kB(如byte[90000])时,它会存储在大对象堆中。

就像Gabe所说:

我可以看到,也许大对象堆会变得碎片化,你将无法在某个点之后扩展字典

当LOH被分段时,有时没有足够的空间来存储具有连续地址的对象。这就是导致OutOfMemory异常的原因。更像是LOH中连续空间不足的异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接