如果GC完成了它的工作,为什么我们会遇到内存不足异常

本文关键字:我们 为什么 遇到 异常 内存不足 工作 GC 如果 | 更新日期: 2023-09-27 18:14:46

通常我被告知GC比我们所有人都聪明,不要乱用它,所以我想当内存不足时,它会去为程序释放内存。那么为什么我有时仍然会在大对象上出现内存异常?

目前,我再次接近另一个可能耗尽内存的场景。我有一个类级字典,包含一些类的列表,它有20万个键。我必须将它保持在全局级别,所以在我的某个方法中我需要它,但是一旦我在那个方法中使用了它,我就想释放它的空间。将其设置为空是否足够?

private void Foo()
{
   // do stuff with MY_DICTIONARY
   MY_DICTIONARY = null  // ?
   // whew! freeded up memory or not?
}

如果GC完成了它的工作,为什么我们会遇到内存不足异常

对于何时调用GC是不确定的。在实例不再使用内存时释放内存并不是一个直接的问题,它发生在计算机足够开放的时候。在许多情况下,这可以早一点而不是晚一点,但仍然不能保证它会立即发生。

这就是像GC.Collect()这样的方法存在的原因。在大多数情况下,我们不必担心,因为我们正在处理足够小的变量和足够的RAM,"最终"对我们的使用来说绰绰有余。垃圾收集器是作为一种实用工具而不是神来提供的。它很有用,因为它几乎适用于所有情况(特别是在应用程序情况下)。在大多数情况下,我们不必担心存储那么多数据(如果我们在应用程序中这样做,我们可能需要检查我们的设计,以实现一些分页,以便首先不要一次显示那么多数据)。但是,仅仅因为我们使用托管内存,并不能保证它将以一种可靠的、100%的方式进行管理,也不能保证我们可以向它们扔任何东西。

这就是像c++这样的语言仍然存在的原因之一。正如你所说,GC非常聪明,而且非常擅长自己的工作。所以不需要考虑mallocfree,只是对它们有一些信任,这很好,但这并不意味着它可以读懂我们的思想。

这有点像巡航控制。你可以在路上行驶并设置巡航控制以保持恒定的速度,但当你开始爬坡时,你需要一些时间来赶上并开始加大油门以保持速度。同样地,它会让你比其他方式更快地从山上掉下来。GC知道内存中哪些对象是可释放的,但它不一定知道上下文的细节,其思想是它在我们的代码后面被动地起作用。否则,将导致一些严重的性能问题。

正如许多其他人也注意到的,我认为这在更实际的意义上是重要的,GC当然只会收集不再使用或范围内的资源。因此,如果Dictionary中的元素在其他地方使用(并且通过引用传递,即class),它们将不会被收集。此外,如果您将Dictionary保留为static成员,它将永远不会被收集。

如果你在代码的某个地方保留了它的引用,这是不够的:

// somewhere away 
COPY_DICTIONARY = MY_DICTIONARY ;
.....
MY_DICTIONARY = null ; 
// Won't free the inial dictionary because COPY_DICTIONARY keep referencing the object

据我所知,GC只清理不再需要的资源。如果你有一个128Mb的RAM容量,你创建了一个长度为128000000的字节数组,但仍然在使用它,那么所有可用的内存将被填满,而不管GC如何,然而,当方法声明该数组完成时,GC将清理它并释放资源。(假设内存不足异常没有被抛出)

这将释放Dictionary。但是,只有在没有其他元素保存对它们的引用时,它才会释放字典中保存的元素。

是的,会做的事。任何仍然被引用的对象都不会被视为垃圾,因此垃圾收集不会为该对象释放内存。由于有可能引用具有大量数据的大量对象,而GC只释放未引用的对象,因此仍然有可能出现内存不足异常。

如果作用域中有足够的对象实例来填充它,则会出现内存不足异常。如果你创建一个无限循环,在每个循环中创建一个对象,你最终会耗尽内存;垃圾收集器不能清除您正在创建的对象,因为仍然有代码可以访问它们。

即使有大量的空闲内存,你也可以得到内存异常,特别是如果你使用巨大的对象列表,就像你的情况一样:请看看"大对象堆碎片",看看这种结构是如何在内存中处理的。

为了避免这些错误,您可以使用其他结构来处理巨大的集合(是列表的最小块),并在代码中使用内存分析器来检测故障发生的位置。

清空你的字典是不够的。你的类是否实现了IDisposable ?

如果是,那么当你不再需要一个对象时,你也必须在某个时候调用Dispose(),然后从字典中删除它,这样它们内部使用的非托管资源就会被释放。非托管资源可能包括需要通过这种方式释放的内存使用。

我在收集大量图像时遇到了同样的问题。我最终得到了一个内存不足的异常,因为我在从集合中删除它之前没有调用Dispose()。

我的图片对象也是如此,也适用于所有实现了IDisposable的对象。

如果您拥有字典中类的代码,请查看这个问题,该问题解释了如何详细实现IDisposable