假设有以下代码:
foreach (...)
{
List<int> localList = new List<int>(100);
// do stuff
localList = null;
}
我不时会尝试将引用归零,就像程序即将退出(即返回)一样,或者在这种情况下,就像它要循环一样。这样做有什么好处,即使是小的吗?
答案 0 :(得分:12)
对局部变量执行此操作没有任何好处。 CLR通过JIT确切知道何时局部变量不再在方法中使用并因此可收集。
Raymond Chen最近做了一篇非常深入的博客文章,关于对象何时可以收藏。它详细介绍了这种情况,值得一读
然而,这条规则有几个例外。如果有问题的局部变量被捕获到闭包或迭代器中,那么将变量置零会产生影响。也就是因为本地不再是本地,而是一个字段,并且具有不同的GC语义。
答案 1 :(得分:4)
没有。事实上,在某些情况下,“归零”局部变量可以防止垃圾收集。在正常操作中,只要变量不再可以从任何正在执行的代码访问,它就可用于垃圾收集。如果在你的方法结束时,你将变量“空出”,那么你将它保持“活着”直到那一刻,并且实际上延迟了垃圾收集的可用性。
答案 2 :(得分:2)
如果声明在您所拥有的区块内,则不会。一旦超出范围,参考将自动无效并GC。
答案 3 :(得分:2)
我写了a long and exhaustive blog article answering this question。
总而言之,一般的答案是“不;没有好处”。但是,有一些特殊情况:
IDisposable.Dispose
中将实例字段设置为空的半常见做法不符合此测试,不应鼓励]。总之:一般来说,不建议将变量设置为null以帮助垃圾收集器。如果认为有必要,则存在异常情况,并应在代码中仔细记录。
答案 4 :(得分:2)
如果一个类有一个终结器,那么任何非空的对象引用字段都会使引用的对象保持比它们更长的时间。如果在终结器运行之前已知对象是不必要的,那么清除对象引用字段将允许对象比其他情况更快地收集一代“代”。这可能是一个巨大的胜利。
如果一个对象的生命周期(有用与否)预期比它有一个引用的对象的使用寿命更长,不必要地保持一个引用将阻止后一个对象被收集,直到前者为止(即引用将强制后一个对象即使在它变得无用之后也要保留。清除参考将避免该问题。
如果用vb.net编写的类具有任何“WithEvents变量”,则只要持有它们的对象变得无用,就应该清除它们(将它们设置为空)。当一个类在“WithEvents变量”中保存对活动对象的引用时,不能对其进行垃圾回收。如果是枚举器保存对底层集合的“WithEvents”引用(例如,如果/当集合被更改时它可以接收事件)并且它的Dispose处理程序不清除它对底层集合的引用,枚举器将保持活动,只要底层的集合是。如果集合被枚举很多次,这可能是一次巨大的内存泄漏。
答案 5 :(得分:1)
没有。让GC完成它的工作,只在你需要的时候帮助它。
答案 6 :(得分:0)
有一个重要的情况是nulling有效,即当您使用调试版本时关闭了一些jit优化。关于何时清理对象的许多更有趣的行为可以停止,以使调试更容易。
这很重要,因为它可能导致错误地查看发布版本中发生的事情。
答案 7 :(得分:0)
响应是:是的,如果你知道你在做什么!但它是过早优化(许多人的所有邪恶的根源),所以除非你真的需要记忆,否则你不应该这样做。
现在,我只分析两个可能有用的案例。
您知道的对象的属性将不再被引用(例如,您在私有属性中缓存了一些复杂的计算。您知道您将不再需要,您清理它)但是这个被说了很多人
复杂方法中的字段。这个与其他人所说的相反,但我有一个例子,所以我知道我是对的:-)现在,通常,在顺序方法中,GC可以看到本地的位置引用不再被使用(技术上重要的部分是它不再被读取。并且调用变量的方法是“读取”。写入变量不计算,因为如果没有人会读取新值,那么它就没用了写下来)
但我已经说过顺序方法。和非顺序方法?我们试试吧! (显然我已经完成了!我不会制作 ideone 示例,因为单声道的GC与.NET的GC不同。)
在发布模式下编译这段代码,在没有调试器的情况下编译 (所以CTRL-F5)并观察结果:
using System;
namespace ConsoleApplication17
{
class Program
{
static void Main()
{
const int cycles = 1000000;
{
string str = new string(' ', 10000000);
Console.WriteLine("Let's see when the GC will free the memory");
Console.WriteLine("Start: {0}", GC.GetTotalMemory(true));
for (int i = 0; i < 1000000; i++)
{
if (i == cycles - 1)
{
Console.WriteLine("Near end: {0}", GC.GetTotalMemory(true));
}
//Here we reference the string,
//but only in the first 100 iterations
if (i < 100 && str[str.Length - 1] == 'c')
{
throw new Exception();
}
}
Console.WriteLine("End: {0}", GC.GetTotalMemory(true));
}
Console.WriteLine();
{
string str = new string(' ', 10000000);
Console.WriteLine("Let's do the work for him");
Console.WriteLine("Start: {0}", GC.GetTotalMemory(true));
for (int i = 0; i < 1000000; i++)
{
if (i == cycles - 1)
{
Console.WriteLine("Near end: {0}", GC.GetTotalMemory(true));
}
//Here we reference the string,
//but only in the first 100 iterations
if (i < 100 && str[str.Length - 1] == 'c')
{
throw new Exception();
}
else if (i == 100)
{
str = null;
Console.WriteLine("Just nullified the string: {0}", GC.GetTotalMemory(true));
}
}
Console.WriteLine("End: {0}", GC.GetTotalMemory(true));
}
Console.ReadKey();
}
}
}
结果:
Let's see when the GC will free the memory
Start: 20042264
Near end: 20042888
End: 42872
Let's do the work for him
Start: 20042888
Just nullified the string: 42872
Near end: 42872
End: 42872
解释:这个程序是一个循环程序的例子,其中一个大对象在循环之外被初始化(for
循环)并且大对象仅在循环的一部分中被使用(例如在前100次迭代)。很明显,GC不能轻易地从迭代99(第100次迭代,0-99)结束时看到该对象不会被引用。
但请记住过早优化和所有邪恶的根! : - )