问题描述
我有这个代码:
本质上,我是在尝试演示 c# 终结器的使用并制作一个不会死的对象,我称它为 Zombie.现在,通常这个演示效果很好,但今天我尝试使用与对象初始化程序相同的代码,而不是仅仅分配给属性(在这种情况下为名称).我注意到有区别.也就是说,终结器永远不会被调用,即使我正在尽我最大的努力让垃圾收集器完成它的工作.
谁能解释一下区别,或者我在 C# 编译器中发现了一个错误?
(我在 Win7x64 上的 VS2010 SP1 中使用 C# 4)
谢谢.
使用系统;使用 System.Collections.Generic;使用 System.Linq;使用 System.Text;使用 System.Threading;命名空间僵尸{课堂节目{静态无效主要(字符串 [] 参数){Console.WriteLine("主线程:" + Thread.CurrentThread.ManagedThreadId);//case 1:这就是问题所在.Zombie z = new Zombie { Name = "Guy" };//对象初始化器语法使终结器不被调用.//情况 2:这不会导致问题.终结器被调用.//僵尸z = new Zombie();//z.Name = "家伙";弱参考 weakZombieGuyRef = new WeakReference(z, true);z = 空;GC.GetTotalMemory(forceFullCollection: true);GC.Collect();而(真){Console.ReadKey();if (weakZombieGuyRef.IsAlive){Console.WriteLine("僵尸家伙还活着");}别的{Console.WriteLine("僵尸家伙死了..银弹有人吗?");}Zombie.Instance = null;GC.AddMemoryPressure(12400000);GC.GetTotalMemory(forceFullCollection: true);GC.Collect();}}}公共课僵尸{公共字符串名称 { 获取;放;}公共静态僵尸实例=空;〜僵尸(){Console.WriteLine(Thread.CurrentThread.ManagedThreadId);Console.WriteLine("僵尸调用终结器" + this.Name);锁(类型(僵尸)){实例=这个;GC.ReRegisterForFinalize(this);}}}}
虽然下面的原始答案仍然准确,但看起来它是调试信息和优化的混合,这在这里有所作为.
根据我的实验:
编译器标志结果/o+/debug- 终结器运行/o+/debug+ 终结器运行/o-/debug- 终结器运行/o-/debug+ 终结器*不*运行
<小时>
在使用 /o+
在命令行上编译时,我的机器上仍会调用终结器.我的猜测是您正在调试器中运行 - 这会改变 GC 行为.如果没有调试器,GC 将收集它可以证明永远不会被读取的任何内容.使用调试器,我相信 GC 不会收集任何在堆栈上仍有引用的对象,即使没有 代码 来读取有问题的变量.p>
现在有了对象初始化器,编译器代码在堆栈上包含一个额外的引用.这一行:
Zombie z = new Zombie { Name = "Guy" };
有效:
僵尸 tmp = new Zombe();tmp.Name = "家伙";僵尸 z = tmp;
z
的赋值仅在所有属性设置完成后执行.
我的猜测是这里的 tmp
变量使对象保持活动状态.
I have this code:
Essentially i'm trying to demonstrate the use of the c# finalizer and make an object that cannot die, I called it Zombie. Now, normally this demo works great, but today I tried using the same code with the object initializer instead of just assigning to the property (Name in this case). I noticed there is a difference. Namely that the finalizer never gets called, not even when I'm trying my best to make the Garbage Collector do it's work.
Could someone explain the difference, or have I found a bug in the C# compiler?
(I'm using C# 4 in VS2010 SP1 on Win7x64)
Thanks.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Zombie
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main thread: " + Thread.CurrentThread.ManagedThreadId);
// case 1: this is where the problem is located.
Zombie z = new Zombie { Name = "Guy" }; // object initializer syntax makes that the finalizer is not called.
// case 2: this is not causing a problem. The finalizer gets called.
//Zombie z = new Zombie();
//z.Name = "Guy";
WeakReference weakZombieGuyRef = new WeakReference(z, true);
z = null;
GC.GetTotalMemory(forceFullCollection: true);
GC.Collect();
while (true)
{
Console.ReadKey();
if (weakZombieGuyRef.IsAlive)
{
Console.WriteLine("zombie guy still alive");
}
else
{
Console.WriteLine("Zombie guy died.. silver bullet anyone?");
}
Zombie.Instance = null;
GC.AddMemoryPressure(12400000);
GC.GetTotalMemory(forceFullCollection: true);
GC.Collect();
}
}
}
public class Zombie
{
public string Name { get; set; }
public static Zombie Instance = null;
~Zombie()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Finalizer called on zombie" + this.Name);
lock (typeof(Zombie))
{
Instance = this;
GC.ReRegisterForFinalize(this);
}
}
}
}
EDIT: While the original answer below is still accurate, it looks like it's the mixture of debug information and optimization which makes a difference here.
From my experiments:
Compiler flags Result
/o+ /debug- Finalizer runs
/o+ /debug+ Finalizer runs
/o- /debug- Finalizer runs
/o- /debug+ Finalizer does *not* run
The finalizer is still called on my box, when compiling on the command line with /o+
. My guess is that you're running in a debugger - which changes the GC behaviour. Without the debugger, the GC will collect anything that it can prove will never be read. With the debugger, I believe the GC won't collect any objects which still have references on the stack, even if there's no code to read the variables in question.
Now with an object initializer, the compiler code includes an extra reference on the stack. This line:
Zombie z = new Zombie { Name = "Guy" };
is effectively:
Zombie tmp = new Zombe();
tmp.Name = "Guy";
Zombie z = tmp;
The assignment to z
is only performed after all the properties have been set.
My guess is that the tmp
variable here is keeping the object alive.
这篇关于使用 Object Initializer 的复活区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!