周琪 发表于 2013-5-18 21:22
就是g_initLocal0那儿,这是啥意思。还有你说的中间变量是指?。。。
告诉我下谢谢。。。 ...
先看我写的那段C#代码,除了初始化Person对象的代码不同以外, 其他的完全一样。
但输出的结果却是(参照上一个帖子):上半部分代码创建的弱引用,被回收。下半部分代码创建的弱引用没有被回收。
既然只有两句代码不同,那问题的关键肯定就是这不同的代码啦:- <span style="background-color: rgb(255, 255, 255); ">Person</span><span style="background-color: rgb(255, 255, 255); "> </span>f1 = new <span style="background-color: rgb(255, 255, 255); "> Person </span>();
- f1.Name = "John";
复制代码- Person p = new Person() { Name = "qq" };
复制代码 上面两段代码分别定义了两个对象,f1和p。(为了更好理解,这里我修改了:Person f1, Person p)
f1是用无参的构造函数创建的对象,然后为对象的Name属性赋值。
p是用初始化器来创建的对象,并设置Name属性。
为了确认为什么第二段代码中弱引用没有被回收,我使用 Reflector7.3 进行了反汇编:
【1】部分:
1.先看这行代码 PersonTwo f1 = new PersonTwo();
对照C#源码不难看出,程序首先创建一个 PersonTwo f1变量,然后使用new PersonTwo 申请了一片内存,并创建了一个PersonTwo实例,最后通过等号运算符 [f1 = 地址] ,将堆内存中创建的PersonTwo实例地址保存在f1变量中。
2.代码 Name = "John";
为堆内存中实例的Name属性赋值"John"。
【2】:
1.代码 Person <>g__initLocal0 = new Person();
同样,先创建了一个 Person 类型的变量 <>g__initLocal0(这是变量的名字,虽然它不符合命令规则),而后在堆内存中创建一个 Person类的实例,并将指向这个实例的地址赋值给 <>g__initLocal0 。
2.代码 Name = "qq";
为堆内存中实例的Name属性赋值"John"。
3.Person p =<>g__initLocal0;
将变量 <>g__initLocal0 中保存的地址赋值给 p。此时,p与<>g__initLocal0指向了同一个内存位置了。。。
通过对照【1】、【2】,再结合我们写的C#源码,我们发现了问题的所在:
由于.Net程序是通过IL代码来执行的,那么反汇编后得到的"C#代码"(上图)就是程序执行时的情况,【1】中的代码和我们写的程序源码完全相同;而【2】中的代码在执行时,程序自己创建了一个中间变量<>g__initLocal0 ,由于这个变量同样的指向我们申请的那片内存,所以之后调用GC.Collect()当然就不会被回收啦。。。
{:soso_e113:}
----------------------------------------------------分割线----------------------------------------------------
下面我还想知道编译器为什么要给我加上这么一个中间代码,百度了一下后才发现,这只是C#中为了使程序员少打一行代码而自动处理的(Person p = new Person() { Name = "qq" };原来是不是需要两行代码?),另一方法也是为了更好得支持匿名类型。
参考资料:
不能不说的C#特性-对象集合初始化器
http://kb.cnblogs.com/page/42575/2/
|