简短回答,不,o 将被释放。不用担心。
答案稍长:
您的代码或多或少做了以下事情:
- 在该线程上创建一些本地存储以引用新的 MyClass (
o)。
- 创建新的 MyClass
- 将新 MyClass 的引用存储在
o。
- 从 lambda 创建一个新委托。
- 将该委托分配给事件(
o 现在具有对委托的引用)。
在此期间的任何时候,GC 都可能会停止线程,然后检查哪些对象是有根的或没有根的。根是静态对象或线程本地存储中的对象(我的意思是给定线程执行中的局部变量,由堆栈实现,而不是本质上是静态形式的“线程本地存储”)。有根对象是根、它们引用的对象、它们引用的对象等等。有根对象不会被收集,其余的会被收集(除非我们暂时忽略一些与终结器有关的额外内容)。
在创建 MyClass 对象之后到目前为止,它还没有被线程的本地存储植根。
在创建委托对象之后到目前为止,它没有被线程的本地存储或 MyClass 对它的引用所植根。
现在,接下来会发生什么?
这取决于。 lambda 不会让 MyClass 保持活动状态(MyClass 保持活动状态,但是当 MyClass 消失时,lambda 也会保持活动状态)。这还不足以回答“'o' 是否有资格在这里进行垃圾收集?”不过。
void Meth0()
{
var o = new MyClass();
o.MyClassEvent += (args) => {};
}//o is likely eligible for collection, though it doesn't have to be.
void Meth1()
{
int i = 0;
{
var o = new MyClass();
o.MyClassEvent += (args) => {};
}//o is likely not eligible for collection, though it could be.
while(i > 100000000);//just wasting time
}
void Meth2()
{
{
var o = new MyClass();
o.MyClassEvent += (args) => {};
int i = 0;//o is likely eligible for collection, though it doesn't have to be.
while(i > 100000000);//just wasting time
}
}
void Meth3()
{
{
var o = new MyClass();
o.MyClassEvent += (args) => {};
var x0 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x1 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x2 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x3 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x4 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x5 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x6 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x7 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x8 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x9 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x10 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
var x11 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be.
int i = 0;
while(i > 100000000);//just wasting time
}
}
Meth0 看似最简单,但实际上并非如此,所以我们会回到它。
在 Meth1 中,实现可能保留本地存储,因为它不再需要,所以虽然 o 不在范围内,但实现仍然有正在使用的本地存储。
在 Meth2 中,实现可以使用与 o 和 i 相同的本地存储,因此即使它仍在范围内,它也有资格收集(“在范围内" 表示程序员可以选择用它做某事,但他或她没有,编译后的代码不需要这样的概念)。
Meth3 更有可能重复使用存储空间,因为它的额外临时使用使得该实施与一开始就将所有存储空间搁置相比更有意义。
这些事情都必须变成这样。 Meth2 和 Meth3 都可以在开始时预留该方法所需的所有存储空间。 Meth1 可以重用存储空间,因为重新排序分配的 i 和 o 没有区别。
Meth0 更复杂,因为它可能取决于调用方法接下来对本地存储执行的操作,而不是当时是否进行清理(两者都是合法的实现)。 IIRC 总是对当前的实现进行清理,但我不确定,反正也没关系。
总而言之,范围不是相关的东西,而是编译器和后来的 JITter 是否可以并且确实使用与对象相关的本地存储。甚至可以在调用对象的方法和属性之前清理对象(如果这些方法和属性不使用 this 或对象的任何字段,因为如果对象已经已删除!)。