C#.NET对象处理

时间:2010-01-21 16:55:21

标签: c# .net object garbage-collection dispose

应该是一个简单的。假设我有以下代码:

void Method()
{
   AnotherMethod(new MyClass());
}

void AnotherMethod(MyClass obj)
{
   Console.WriteLine(obj.ToString());
}

如果我调用“Method()”,那么在该过程中创建的MyClass对象会发生什么?它是否仍然存在于调用后的堆栈中,即使没有使用它?或者它会立即被删除吗?

我是否必须将其设置为null才能让GC更快地注意到它?

7 个答案:

答案 0 :(得分:13)

对Method的调用完成后,您的MyClass对象仍处于活动状态,但是没有从root值中引用它。因此它将一直存在,直到下一次GC运行,它将被收集并回收内存。

除了强制GC之外,除了强制GC之外,您无法做任何事情来加速此过程。然而,这可能是一个坏主意。 GC旨在清理此类对象,并且您为使其更快而进行的任何尝试都可能导致整体速度变慢。您还会发现GC在正确清理托管对象的同时,实际上可能不会减少系统中的内存。这是因为GC保留它以备将来使用。这是一个非常复杂的系统,通常最好留给它自己的设备。

答案 1 :(得分:7)

  

如果我调用“Method()”,那么在该过程中创建的MyClass对象会发生什么?

它在GC堆上创建。然后,在堆中放置对其在堆中的位置的引用。然后调用AnotherMethod。然后调用对象的ToString方法并打印出结果。然后AnotherMethod返回。

  

调用后它是否仍然存在于堆栈中,即使没有使用它?

你的问题很模糊。 “通话”是指对Method或AnotherMethod的调用?它有所不同,因为此时,堆内存是否是垃圾收集的候选者取决于您是否通过打开或关闭优化进行编译。我将略微改变你的程序来说明差异。假设你有:

void Method() 
{ 
   AnotherMethod(new MyClass()); 
   Console.WriteLine("Hello world");
} 

关闭优化后,我们有时会生成如下代码:

void Method() 
{ 
   var temp = new MyClass();
   AnotherMethod(temp); 
   Console.WriteLine("Hello world");
} 

在未优化的版本中,运行时实际上会选择将对象视为不可收集,直到在WriteLine之后返回Method。在优化版本中,运行时可以选择在AnotherMethod返回之前,在WriteLine之前将对象视为可收集的。

产生差异的原因是因为在调试会话期间使对象生命周期更容易预测通常可以帮助人们理解他们的程序。

  

或者它会立即被删除吗?

没有立即收集任何东西;当垃圾收集器感觉它应该运行时运行。如果您需要在完成后立即清理某些资源(如文件句柄),请使用“使用”块。如果没有,那么让垃圾收集器决定何时收集内存。

  

我是否必须将其设置为null才能让GC更快地注意到它?

您是否必须将内容设置为null?你有什么变量?

无论如何,你没有做任何事情来使垃圾收集器工作。它在没有你提示的情况下自行运行。

我认为你正在过度思考这个问题。让垃圾收集器做它的事情,不要强调它。如果你的内存没有得到及时收集的真实问题,请向我们展示一些说明问题的代码;否则,只需放松并学会喜欢自动存储回收。

答案 2 :(得分:3)

实际上,实例将在堆上声明,但请查看Eric Lipper的文章The Stack is an Implementation Detail

在任何情况下,因为在函数执行后将不再有对实例的引用,所以垃圾收集器在某个未定义的点处将删除(或者更确切地说,可以)在将来。至于何时发生,它是未定义的,但你(基本上)也不需要担心它; GC有复杂的算法,可以帮助它确定收集的内容和时间。

答案 3 :(得分:3)

  

调用后它是否仍然存在于堆栈中

语义在这里很重要。您询问方法调用后它是否仍然存在于堆栈中。答案是“不”。它已从堆栈中删除。但这不是最后的故事。该对象仍然存在,它不再是根源。在GC运行之前,它不会被销毁或收集。但在这一点上,它不再是你的担忧。 GC决定何时收集的东西要比你或我好得多。

  

我是否必须将其设置为null才能让GC更快地注意到它?

无论如何,几乎从来没有一个很好的理由这样做。唯一有用的是,如果你有一个非常长的运行方法和一个早期完成的对象,否则在方法结束之前不会超出范围。即使这样,将其设置为null也只会在GC决定在方法期间运行的极少数情况下起作用。但在这种情况下,你可能也会做其他错误。

答案 4 :(得分:2)

在C#中,新MyClass()的作用域仅限于在Activity()处于活动状态时。一旦AnotherMethod()完成执行,它就超出了范围并被取消了。然后它将保留在堆上,直到GC运行其收集周期并将其标识为未引用的内存块。所以它仍然在堆上“活着”但它无法访问。

答案 5 :(得分:1)

GC会跟踪代码中可能仍会引用的对象。然后,它会定期检查是否有任何仍然存活的对象,以后可能无法在代码中引用它们,并清理它们。

这种机制有些复杂,当这些收集发生时,取决于各种因素。 GC旨在在最佳时间(它可以建立)完成这些集合,因此,虽然可以强制它进行集合,但这几乎总是一个坏主意。

将变量设置为null将对对象处理的时间产生很小的整体影响。虽然在某些小角落的情况下它可以带来好处,但是不值得为冗余的分配乱丢你的代码,这不会影响你的代码性能,只会损害你的可读性。

GC的设计尽可能有效,无需您考虑。说实话,你真正需要注意的唯一事情就是在分配很长时间保持活力的大型物体时要小心,而这在我的经验中通常是非常罕见的。

答案 6 :(得分:-4)

据我所知,该对象仅在方法上下文中有效。在执行方法“Method()”之后,它将被添加到dispose队列。