什么时候对象有资格进行垃圾回收?

时间:2013-07-05 23:07:21

标签: c# garbage-collection

在约瑟夫·阿尔巴哈里的书中简要介绍了C#5.0,我找到了这个

enter image description here 一旦你传递了最后一个变量使用的代码行,它就会说它所引用的对象有资格进行垃圾收集(即如果没有其他变量持有对该对象的引用)。

然而根据加州大学伯克利分校的this讲座,只要对象上存在对象的引用,它就不会被垃圾收集。我的理解是,在方法返回之前,变量保留在堆栈上。这意味着它引用的任何对象都是活动的,直到该方法返回。

这是本书中的错误还是java和.net垃圾回收的工作方式不同?

4 个答案:

答案 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