在约瑟夫·阿尔巴哈里的书中简要介绍了C#5.0,我找到了这个
一旦你传递了最后一个变量使用的代码行,它就会说它所引用的对象有资格进行垃圾收集(即如果没有其他变量持有对该对象的引用)。
然而根据加州大学伯克利分校的this讲座,只要对象上存在对象的引用,它就不会被垃圾收集。我的理解是,在方法返回之前,变量保留在堆栈上。这意味着它引用的任何对象都是活动的,直到该方法返回。
这是本书中的错误还是java和.net垃圾回收的工作方式不同?
答案 0 :(得分:6)
你是对的。您缺少的是堆栈中不再存在引用。然而根据加州大学伯克利分校的这个讲座,只要对象上存在对象的引用,它就不会被垃圾收集。
对于在堆栈上构造对象的代码:
StringBuilder ref1 = new StringBuilder("object1");
变量ref1
存储在堆栈中的某些内存位置:
0x403730:
Stack Pointer -> 0x40372C: pointer to ref1
0x403728: saved value of EBP
0x403724: saved value of return address
0x403720
现在是下一行:
StringBuilder ref2 = new StringBuilder("object2");
指向ref2
的指针将存储在哪里?在堆栈上:是的。但是在哪里在堆栈上?在当前用于ref1
的相同内存位置!:
0x403730:
Stack Pointer -> 0x40372C: pointer to ref2
0x403728: saved value of EBP
0x403724: saved value of return address
0x403720
简单地将另一个值推送到堆栈上将是愚蠢:
Stack Pointer -> 0x403730: pointer to ref2
0x40372C: pointer to ref1
0x403728: saved value of EBP
0x403724: saved value of return address
0x403720
这很愚蠢,因为不再需要ref1
了。
这就是为什么ref1
有资格进行垃圾收集的原因:不再有任何对它的引用。
答案 1 :(得分:5)
这本书是对的。
在.NET中,垃圾收集器具有代码中使用变量的位置的信息,并且一旦它未使用,该对象就有资格进行垃圾收集。
(但是,如果您运行附加了调试器的代码,垃圾收集器会更改行为。它会保留变量整个范围的对象,而不仅仅是使用变量的位置,以便可以在调试器。)
答案 2 :(得分:4)
我的理解是,在方法返回之前,变量保留在堆栈上。这意味着它引用的任何对象都是有效的,直到方法返回。
JIT可以在上次使用后随时删除对象引用(“变量”),因此不一定如此。
只要对象上存在对象的引用,它就不会被垃圾回收
这是事实 - 但是当这个变量不再“存在于堆栈中”时,JIT可能会以不一定与您的代码匹配的方式发生变化。
在C#5中,这也会让人感到困惑,因为async
方法可能会被变换的方式重写,这些方法比某些情况下的预期更长。
话虽如此,如果您需要保证某个对象在某些时候符合条件,那么将引用该对象的变量设置为null
会明确允许您控制它何时显式合格。
答案 3 :(得分:2)
这是从该方法生成的CIL代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 46 (0x2e)
.maxstack 2
.locals init ([0] class [mscorlib]System.Text.StringBuilder ref1,
[1] class [mscorlib]System.Text.StringBuilder ref2,
[2] class [mscorlib]System.Text.StringBuilder ref3)
IL_0000: nop
IL_0001: ldstr "object1"
IL_0006: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(string)
IL_000b: stloc.0 //here ref1 is stored on stack.
IL_000c: ldloc.0 //here it is loaded to be displayed by the console.writeline
IL_000d: call void [mscorlib]System.Console::WriteLine(object)
IL_0012: nop //from here on no more reference to ref1.
IL_0013: ldstr "object2"
IL_0018: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(string)
IL_001d: stloc.1
IL_001e: ldloc.1
IL_001f: stloc.2
IL_0020: ldloc.2
IL_0021: call void [mscorlib]System.Console::WriteLine(object)
IL_0026: nop //and from here on ref2 and ref3 also.
IL_0027: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_002c: pop
IL_002d: ret
} // end of method Program::Main