假设我有类似的东西:
public class Item
{
public string Code;
public List<string> Codes = new List<string>();
}
private void SomeMethod()
{
List<Item> Items = new List<Item>();
for (int i = 0; i < 10; i++)
{
Item NewItem = new Item();
NewItem.Code = "Something " + i.ToString();
NewItem.Codes.Add("Something " + i.ToString());
Items.Add(Item);
}
//Do something with Items
}
我正在实例化 Item,而不是释放它,因为我需要稍后在列表中访问它(粗略的示例)。
我想知道的是,当
SomeMethod()
完成执行时,项目(及其内容 - 包括 List<>)是否会被取消引用并允许垃圾收集器在运行时清理内存?基本上,这部分代码是否会导致任何内存泄漏,或者当 SomeMethod()
完成处理时是否应该取消引用所有内容。
我的理解是,当没有任何东西保存对对象的引用时,它将被垃圾收集,所以在我看来,这段代码应该没问题,但我只是想确保我理解正确。
编辑:
如果我要添加其中一个对象,
Items
将保留到仍在范围内的另一个列表中(例如全局列表)。会发生什么?
一旦您的变量
Items
超出范围,垃圾收集器确实会在闲暇时收集它(及其内容,因为您的代码已编写)。
垃圾收集器将收集代码无法再访问的任何内容。由于您将无法再访问您的项目列表或列表中包含的任何项目,因此垃圾收集器将在某个时候收集它们。您对垃圾收集器的理解是正确的。
好的,
List<Item> Items
在方法的范围内声明。因此,当方法结束时,它会超出范围,并且列表会被取消引用。
此后,当垃圾收集器认为合适时,内存将被释放。
顺便说一句,由于
Items
是在“本地”范围内声明的,我更愿意将其称为 items
。
方法中的所有变量都在一个栈帧中,并且在方法执行后所有这些变量将与栈帧一起被销毁。 也就是说,执行 SomeMethod() 后,变量 Items 将不再存在,因此 new List() 将被标记为“可以收集”。
第二个问题是,有一个全局列表变量保存了变量 Items 中的其中一个对象的引用,那么执行 SomeMethod() 后,SomeMethod 中的 List 就会被标记为“可以收集” ,并且除了由 globle 列表变量引用的项目之外的所有其他对象也将被标记为“可以收集” 原因是,列表实际上保存了指向堆中确切对象的引用 所以共享对象无法被收集,因为它被globleList引用了
为了保持理智并保住其工作,GC 必须承诺两件事:
垃圾将被收集。这看起来很明显,但如果 GC 不承诺清理托管对象而无需你告诉它,那就毫无用处。
ONLY垃圾将会被收集。你不必担心GC清理仍在运行的对象。
因此,一旦
Items
超出范围(即函数返回后),就无法再通过运行代码访问它,因此无需执行任何操作即可对其进行收集。由于列表中的项目也不再可访问(它们唯一的链接在列表中),因此它们也符合条件。但是,如果您返回了对列表中某个条目的引用,则该对象仍在运行并且还无法被收集。 GC 会做正确的事。
事实上,GC 几乎总是做正确的事情。您实际上只需要担心三种主要情况:
List
一样工作)。就用户而言,容器中不再“存在”的任何对象通常应设置为 null,这样 GC 就不会认为它们可以通过您的集合访问,并错误地使它们保持活动状态。例如,如果您从集合中删除一个项目,请将引用清空或用另一个项目覆盖它。Dispose
块内使用它,否则事情会变得非常奇怪。 (如果您只使用 .net API,那么您是相当安全的。但是丢弃仍然是良好的礼貌。)