假设我有
void foo () {
Bar bar = new Bar(); // bar is never referred to after this line
// (1)
doSomethingWithoutBar();
}
在(1),是对象 bar
指着 合格 垃圾收集?或者 bar
还必须超出范围?它是否有所作为 GC.Collect
被称为 doSomethingWithoutBar
?
这与知道Bar是否有(C#)析构函数或者像这样的类似的东西有关。
一旦确定不再使用对象,对象就有资格进行垃圾收集。这完全有可能 bar
将在变量超出范围之前进行垃圾收集。
证明:
using System;
class Bar
{
~Bar() { Console.WriteLine("Finalized!"); }
}
class Program
{
static void Main(string[] args)
{
Bar bar = new Bar();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}
跑进 发布模式 (因为它没有在调试模式下收集)。
输出:
敲定!
按任何一个键退出...
它也适用 ideone 它使用Mono。输出是一样的。
从快速阅读规范看,它看起来像是特定的实现。它的 允许 垃圾收集它,但不是 需要 至。
我从第10.9节“自动内存管理”中的说明中得到了这个 ECMA规范:
[注意:实现可能会选择分析代码来确定
对象的引用可以在将来使用。对于
例如,如果范围内的局部变量是唯一存在的
引用一个对象,但永远不会引用该局部变量
在当前执行的任何可能的继续执行中
在程序中指出, 实施可能(但不是必需的)
to)将对象视为不再使用。结束说明]
强调我的。
如果没有定义您所指的CLR的版本,那就是 不可能很难确定你在这里看到的行为。
一个 假想 在此示例中,CLR可以假设以下情况属实:
- 的构造函数
Bar
什么也没做
- 没有初始化的字段(即对象构造没有潜在的副作用)
完全无视这条线 Bar bar = new Bar();
因为它“无所事事”而优化它。
就我的记忆而言,在当前版本的CLR中 bar
在构建之后,有资格进行垃圾收集。
马克回答了这个问题,但这是解决方案:
void foo () {
Bar bar = new Bar(); // bar is never referred to after this line
// (1)
doSomethingWithoutBar();
GC.KeepAlive(bar); // At the point where you no longer need it
}
这肯定会发生。例如,这是一个演示,当您仍在执行其构造函数时,可以最终确定实例:
class Program
{
private static int _lifeState;
private static bool _end;
private sealed class Schrodinger
{
private int _x;
public Schrodinger()
{
//Here I'm using 'this'
_x = 1;
//But now I no longer reference 'this'
_lifeState = 1;
//Keep busy to provide an opportunity for GC to collect me
for (int i=0;i<10000; i++)
{
var garbage = new char[20000];
}
//Did I die before I finished being constructed?
if (Interlocked.CompareExchange(ref _lifeState, 0, 1) == 2)
{
Console.WriteLine("Am I dead or alive?");
_end = true;
}
}
~Schrodinger()
{
_lifeState = 2;
}
}
static void Main(string[] args)
{
//Keep the GC churning away at finalization to demonstrate the case
Task.Factory.StartNew(() =>
{
while (!_end)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
});
//Keep constructing cats until we find the desired case
int catCount = 0;
while (!_end)
{
catCount++;
var cat = new Schrodinger();
while (_lifeState != 2)
{
Thread.Yield();
}
}
Console.WriteLine("{0} cats died in the making of this boundary case", catCount);
Console.ReadKey();
}
}
为了使其工作,您需要发出Release版本并在Visual Studio外部运行它(否则调试器会插入阻止该效果的代码。)我已经使用VS 2010目标.NET 4.0 x64对此进行了测试。
您可以在“保持忙碌”循环中调整迭代,以影响Cat完成构建之前完成的清理概率。