C# 特例寿命分析
如果我有C# 特例寿命分析,c#,garbage-collection,C#,Garbage Collection,如果我有 void foo () { Bar bar = new Bar(); // bar is never referred to after this line // (1) doSomethingWithoutBar(); } 在(1)处,指向垃圾收集的对象条是否符合条件?还是bar也必须不在范围之内?如果doSomethingWithoutBar调用GC.Collect,会有区别吗 这与了解Bar是否有(C#)析构函数或类似的东西有关。一旦确定不再使用对象,它
void foo () {
Bar bar = new Bar(); // bar is never referred to after this line
// (1)
doSomethingWithoutBar();
}
在(1)处,指向垃圾收集的对象条是否符合条件?还是bar
也必须不在范围之内?如果doSomethingWithoutBar调用GC.Collect
,会有区别吗
这与了解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();
}
}
在发布模式下运行(因为它不会在调试模式下被收集)
输出:
Finalized!
Press any key to exit...
定稿!
按任意键退出。。。
它也适用于使用单声道的系统。输出是相同的。如果不定义您所指的CLR版本,就很难确定您将在此处看到的行为
在本例中,假设的CLR可以假设以下情况为真:
Bar
的构造函数不执行任何操作
没有初始化的字段(即,对象构造没有潜在的副作用)
完全忽略行Bar=newbar()编码>并优化它,因为它“什么都不做”
就我的记忆而言,在当前版本的CLR中,bar
可以在构建之后立即进行垃圾收集。快速阅读规范,它看起来是特定于实现的。允许对其进行垃圾收集,但不要求进行垃圾收集
我从以下第10.9节“自动内存管理”中的注释中得到:
[注意:实现可能会选择分析代码以确定
将来可以使用对对象的哪些引用。对于
实例,如果作用域中的局部变量是唯一存在的
引用对象,但该局部变量从未被引用
在当前执行的任何可能的继续执行中
在过程中,实现可能(但不是必需的)
将对象视为不再使用。结束注释]
强调我的问题。马克回答了这个问题,但以下是解决方案:
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();
}
}
类程序
{
私有静态int_lifeState;
专用静态bool_end;
私人密封类薛定谔方程
{
私人互联网;
公共薛定谔方程()
{
//这里我用的是“this”
_x=1;
//但现在我不再提及“这”
_生命状态=1;
//保持忙碌,为GC提供收集我的机会
对于(int i=0;i
{
while(!\u结束)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
});
//继续构建猫,直到我们找到想要的案例
int catCount=0;
while(!\u结束)
{
catCount++;
var cat=新的薛定谔方程();
而(_lifeState!=2)
{
螺纹屈服强度();
}
}
WriteLine({0}只猫在制作这个边界案例时死亡),catCount);
Console.ReadKey();
}
}
为了使其正常工作,您需要发出一个发布版本并在Visual Studio外部运行它(否则调试器会插入阻止此效果的代码)。我已经使用VS 2010 Targeting.NET 4.0 x64对其进行了测试
你可以调整“保持忙碌”循环中的迭代次数,以影响猫在完成构建之前结束的概率。我只想等待Eric Lippert过来处理这个问题…@StefanH,我想我已经看到Eric对一个非常类似的问题的回答(或者他的博客)但是我的google fu现在失败了=(@Rob-我知道!我试着在google上搜索“C#Garbage Collection”,因为我认为他将是第一个结果。仅供参考,这些情况下的常见做法(或者更常见的情况是,当非托管资源需要保持活动状态时),将要使用。我认为它将等待作用域完成,以确保没有对该局部对象的任何运行时引用。@Tigran,因为它是一个局部变量,在运行时怎么可能有对它的引用?=)我无法重现这种情况。在任何版本的框架中:2、3.0、3.5、4.0。@Hailton:您是否尝试在发布模式下运行它?我使用Visual Studio C#2010 Express(C#4)对其进行了测试在发布模式下。您可以单击ideone链接并查看输出-它也在那里工作。我还简化了代码。您只需将其复制并粘贴到新的控制台项目中,切换到发布模式,然后按F5。我使用这些说明更改Visual Studio Express以在发布模式下生成:是的,您是对的。在发布模式下,我将因为我能够重现这种情况。谢谢你的澄清。我有一个问题:我做了一个类似的代码,但不能做GC来收集对象。我将用这段代码作为一个新问题发布。我想你是指一个“假想的编译器”@StefanH,如果“假想的编译器”你是指“假想的JIT编译器”,那么是的,这就是我的意思=)否则Bar
可以在另一个程序集中定义,可以在编译后替换掉csc
compile,导致编译时的Bar
被剥离,即使运行时的Bar
可能会做些什么…(将插件开发视为一种可能的场景)我更多的是想在创建CLR之前,也就是在编译时,它会被剥离掉,对吗?编辑:不,我只是错了(CLR并不是我所想的意思),我想它可能会在生成CIL之前被剥离掉。CLR