在 .NET List<T> 中将项目设置为 null 是否使其可用于垃圾收集,这是一个好主意吗?

Does setting an item null in a .NET List<T> make it available for Garbage Collection and is it a good idea?

假设我有一个很大的列表,其中每个项目都被处理一次,然后在长时间的操作中不再查看:

List<T> items;
 // ... some stuff is done with the list then finally

 for(int i = 0; i < items.Count; i++)
 {
     SomeOperation(items[i]);
     //items[i] never used again at this point
     // say i do this:
     // items[i] = null;

 }

如果我取消注释 items[i] = null,这是否会解除索引 i 处的对象并使其可用于垃圾收集?

从内存使用的角度来看,这样做与让 GC 在整个列表未被使用时稍后发生相比会更有效吗?

这忽略了一些问题,例如稍后更改代码,结果发现这些项目后来被使用,意外的空毁破坏。

如果对该对象的唯一 root 可访问引用是通过该列表,那么是的,将该引用设置为 null 将使该对象 有资格 进行垃圾回收.

当垃圾回收最终发生时,如果该对象最终被清理,而列表本身需要保留,那么您将减少应用程序的内存占用量,减少该对象的大小。

请注意,为引用列表中的对象分配的内存仍然存在;只有它引用的对象的内存可以被清理。

正如许多其他人所提到的,您对该数据结构的使用强烈表明您首先不应使用 List,而应使用 Queue 或其他类似的数据结构,因为那样会更有效地适合您的使用。

您还需要记住,内存很便宜;人们往往拥有很多。除非您有成千上万的对象,否则这些对象会长时间保持活动状态,否则这种改进不太可能足以让您注意到它;您甚至可能无法衡量差异。使用正确的数据结构会给您带来所有这些好处,而且 可以使代码显着简化,并且 readable/maintainable.

是的,如果列表最后引用了一个对象设置元素,给定索引为 null,则对象符合 GC 条件。

这不是个好主意:

  • 代码看起来会很奇怪。可能很好的评论 why 它完成了可以提供帮助。
  • 可能有更好的方式来表达这种行为。

如果您尝试将元素设置为 null 并且分析显示您正在寻找的 performance/scalability/whatever 有可测量的增加 - 考虑重写代码以使用其他一些更合适的结构,更好地表达列表的临时性质(可能是队列,堆栈或只是 IEnumrable 惰性评估)

什么时候可能有益(确保通过分析来证明):

  • list 包含大型托管对象,如 Bitmaps 或 MemoryStream。在这种情况下,GC 有很好的机会实际自动 运行 并清理对象(假设在迭代期间有更多分配)
  • 列表包含持有大型非托管对象的小型托管对象(如全局分配的内存句柄)- 可能需要设置为 null + 适当的 Dispose + 强制 GC 以触发早期清理(GC 不会检测非托管内存压力)
  • list 用作某些数据结构的后备存储,如自定义 Stack、Queue 或 CircularBuffer 作为您自己的库的一部分 - 将元素设置为 default(T) / null 是一种聪明的方式来防止您的自定义数据结构以延长您的类型不再使用的对象的生命周期。

请注意,在实现自定义数据类型的情况下,由于数据类型的一般约定而不是由于分析,您可能必须这样做 - 即预计 Stack 在 "Pop" 之后完全忘记对象。代码应该在 internalBuffer[i]=null; 旁边有适当的注释 - 即 "avoid holding reference after object removed from Stack".

正如其他地方所讨论的,设置为 null 应该允许对象被垃圾回收。

你在评论里说

I considered using another data structure like a queue, but that did not seem natural in the context of the other code where as a list does.

您始终可以从列表中初始化 Queue

List<T> list = GetList<T>();

Queue<T> queue = new Queue<T>(list); // O(n)

list.Clear(); // to free up memory as per requirement

CLR Profiler 可用于查找分配给函数、类 和程序集的内存以评估性能。

部分参考资料:

.NET Best Practice No: 1:- Detecting High Memory consuming functions in .NET code

Monitoring the Activities of Garbage Collection in .NET Using CLR Profiler