public void func1()
{
object obj= null;
try
{
obj=new object();
}
finally
{
obj = null;
}
}
在大型对象的内存管理方面,为finally块中的引用赋值是否有任何优势?
答案 0 :(得分:3)
让我们在这里处理明确和隐含的问题。
问:首先,当您完成本地变量时,是否有必要将null
分配给它?
A:不,完全没有。当使用优化编译而不是在调试器下运行时,JITter知道正在使用变量的代码段,并且当您通过该段时将自动停止将其视为根。换句话说,如果您为变量指定了某些内容,然后在某个时刻再也没有从中读取过,那么即使您没有明确地将其设置为null
,也可能会收集它。
所以你的例子可以安全地写成:
public void func1()
{
object obj = new object();
// implied more code here
}
如果"中没有代码,则此处隐含更多代码"永远访问obj
变量,它不再被视为根。
请注意,如果在非优化程序集中运行,或者将调试程序连接到进程,则会发生更改。在这种情况下,变量的范围被人为地扩展,直到它们的范围结束,以便更容易调试。
问:其次,周围班级的字段怎么样?
A:这里肯定会有所作为。
如果方法周围的对象长时间保持活动状态,并且对字段内容的需求已经消失,则是,将字段设置为null
将使其引用的旧对象有资格收藏。
所以这段代码可能有价值:
public class SomeClass
{
private object obj;
public void func1()
{
try
{
obj=new object();
// implied more code here
}
finally
{
obj = null;
}
}
}
但是,那你为什么要这样做呢?相反,您应该努力编写不依赖于周围状态的更清晰的代码。在上面的代码中,你应该重构"暗示更多的代码在这里"要在要使用的对象中传递,并删除全局字段。
显然,如果你不能这样做,那么是的,只要不再需要对象引用就将字段设置为null
是一个好主意。
有趣的实验,如果您在LINQPad中运行以下代码并进行优化,那么您对输出的期望是什么?
void Main()
{
var s = new Scary();
s.Test();
}
public class Scary
{
public Scary()
{
Console.WriteLine(".ctor");
}
~Scary()
{
Console.WriteLine("finalizer");
}
public void Test()
{
Console.WriteLine("starting test");
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("ending test");
}
}
回答(当你认为你已经得到它时,鼠标悬停显示):
.ctor
开始测试
终结
结束测试
说明:
由于实例方法的隐式
this
参数从未在方法中使用,因此即使方法当前正在运行,也会收集该方法周围的对象。