在.NET List 中将项设置为null是否可用于垃圾收集,这是一个好主意吗?
假设我有一个大型列表,其中每个项目都被处理一次,然后在长时间操作期间再也没有看过:
List 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在以后未使用整个列表时发生。
这忽略了一些问题,比如稍后更改代码,事实certificate这些项目稍后使用,并且意外的null破坏了。
如果对该对象的唯一可根目录访问是通过该列表,则是,将该引用设置为null
将使该对象符合垃圾回收的条件。
当垃圾收集最终发生时,如果该对象最终被清理而列表本身需要保持不变,那么您将通过该对象的大小减少应用程序的内存占用量。
请注意,为列表中对象的引用分配的内存仍然存在; 它只是它引用的对象的内存,可以清理。
正如许多其他人所提到的,您对此数据结构的使用强烈表明您不应该首先使用List
,而是使用Queue
或其他类似的数据结构,因为这样可以更有效地满足您的使用需求。
你还需要记住,记忆很便宜; 人们倾向于拥有很多。 除非你有成千上万的物品否则会长时间保持活着,所以即使注意到这一点,这也不足以让你有所改善。 你甚至可能无法衡量差异。 使用正确的数据结构可以为您提供所有这些优势,并使代码更加简单,更易读/可维护。
是的,如果列表具有对象设置元素的最后引用,则给定索引为null使对象符合GC的条件。
这不是一个好主意:
- 代码看起来很奇怪。 可能很好的评论为什么它可以帮助。
- 有可能更好的方式来表达这种行为。
如果您尝试将element设置为null并且分析显示性能/可伸缩性/您正在寻找的任何可测量的增加 – 考虑重写代码以使用更好地表达列表的临时性质的其他更合适的构造(可能是Queue,Stack或者只是IEnumrable与懒惰的评价)
当它可能是有益的(确保配置文件certificate):
- list包含大型托管对象,如Bitmaps或MemoryStream。 在这种情况下,GC有很好的机会实际自动运行和清理对象(假设在迭代期间有更多的分配)
- list包含保存大型未管理对象的小型托管对象(如全局分配的内存句柄) – 设置为null +正确可能需要Dispose + force GC来触发早期清理(GC不会检测未管理的内存压力)
- list用作某些数据结构的后备存储,如自定义堆栈,队列或CircularBuffer作为您自己库的一部分 – 将设置元素设置为
default(T)
/null
是一种智能方法,可防止自定义数据结构延长对象的生命周期您的类型使用。
请注意,在实现自定义数据类型的情况下,您可能必须这样做,因为数据类型的一般契约而不是由于分析 – 即预期Stack在“Pop”之后完全忘记对象。 代码应该在internalBuffer[i]=null;
旁边有适当的注释internalBuffer[i]=null;
– 即“避免在从Stack中删除对象后保持引用”。
如其他地方所述,设置为null
应该允许对象被垃圾收集。
你在评论中说
我考虑使用另一种数据结构,如队列,但在列表的其他代码的上下文中,这似乎并不自然。
您始终可以从列表初始化Queue
:
List list = GetList (); Queue queue = new Queue (list); // O(n) list.Clear(); // to free up memory as per requirement
CLR Profiler可用于查找分配给函数,类和程序集的内存以评估性能。
一些参考:
.NET最佳实践No:1: – 在.NET代码中检测高内存消耗函数
使用CLR Profiler监视.NET中垃圾收集的活动