GC运行后对象会发生什么?

时间:2015-10-29 07:14:48

标签: c# .net garbage-collection

考虑以下陈述

void foo()
{
    string text = "something";
    int a = 10;
    int b = a;

    var array = new int[5]{2,1,3,5,4};

    GC.Collect();
}



**Stack**           **Heap**

text = 0x20         0x20 something

a = 10

b = 10

array = 0x50:0x55   0x50 2
                    0x52 1
                    0x53 3
                    0x54 5
                    0x55 4

一些假设:

  1. 在方法结束时调用GC.Collect()。
  2. GC运行(假设)。
  3. 问题是:

    1. 堆中有什么价值?

      我回答了内存位置0x20,0x50-0x55将被回收并删除。他再次问道,记忆位置有什么价值?我猜可能是垃圾值。他说错了。 (WTX?)

    2. 堆栈中的项目会发生什么变化? text,a,b,array包含哪些值?

      我回答说,因为它们包含的引用已被删除,它们也将被回收。 他询问变量现在的值是多少。

    3. 在GC Collection之后重绘上面的表格列。

      GC运行后

      **Stack**           **Heap**
      
      text (cleared)      0x20 Garbage/NULL
      
      a (cleared) 
      
      b (cleared) 
      
      array (cleared)     0x50 Garbage/NULL
                          0x52 Garbage/NULL
                          0x53 Garbage/NULL
                          0x54 Garbage/NULL
                          0x55 Garbage/NULL
      
    4. 结果:他说答案不正确。我想念的是什么想法吗?

3 个答案:

答案 0 :(得分:5)

不可能给出一个确定性的答案,但有些人会考虑这种行为。

首先,所有变量都是局部变量。因此,方法返回后,这些变量超出了范围。内存(或CPU寄存器)中的确切位置现在包含未知内容。其他事情现在可能生活在那个记忆区域,我们不知道。 我们无法知道。因此,他们被清除了吗?谁知道呢。

但是,对于所有意图和目的,方法返回后,不再存在这些变量。

如果您认为“这些变量存在于堆栈中”,please note that the stack is an implementation detail

现在,另外你分配了两个堆对象,一个字符串和一个数组。

由于这些只是在方法返回后超出范围的局部变量引用,因此这些变量不再被认为是有效的,并且有资格进行收集。

然而,这里的“收集”是什么意思?这是否意味着垃圾收集器清除了内存区域?完全没有。

相反,当垃圾收集内存时,它实际上正好相反,它collects living objects。这些对象在内存中被压缩并移动,并且该字符串和该数组所在的区域可被其他对象覆盖。可能是一个只包含零的数组,在这种情况下,内存区域可能看起来“已清除”,但可能不是。

但是,坚持下去,那个字符串,它会发生一些特别的事情。由于字符串是作为文字在源代码中编写的,因此程序启动时实际上是interned。因此,它不会被收集,它仍然存在于内存中的某个地方。换句话说,那个字符串仍然存在,在内存中的某个地方。当变量引用它时,它可能与其他地方相同。 我们无法知道

然而,构成数组对象的字节在压缩活动对象时可被垃圾收集器覆盖。

好的,所有这一切都发生在 方法返回后。

如果GC.Collect()方法返回之前运行垃圾收集周期会发生什么?

好吧,如果您正在使用RELEASE版本,那么上面的所有内容仍然存在。如果在方法中不再使用该变量,那么在引用堆上的某个东西的方法中有局部变量的事实就没有任何意义(即,您已经在执行时间轴中传递了最后一个用法)。

但是,如果您处于DEBUG构建中,或者附加了调试器,则所有局部变量范围都会延长到方法的末尾。因此,它们仍被视为实时引用,并且还将堆上的对象保持活动状态。在这种情况下,不会收集数组。

但是,无论何时运行垃圾收集,堆对象在内存中的位置地址都可能发生变化,因为对象与其他对象一起压缩或者被提升到不同的一代。

以下是您问题的实际答案:

以上大部分内容都是实施细节。我们不应该关心的事情,因为它可能会因优化而改变。

我们可以解释很多事情,但是我们不应该如何处理实际的底层内存。

你在评论中说你以为你是在参加编剧作家面试,我会说这种感觉是正确的。如果您要访问Microsoft的JIT或内存管理团队,那么至少可以将这些内容的一些知识作为工作的一部分进行教学,但如果您已经知道,可能会获得奖励。

但是,对于普通的.NET程序员来说,这是不必要的。其他聪明的人关心这一点。

答案 1 :(得分:2)

以前被对象占用的内存可能会发生三种可能的事情之一:

  • 被另一个对象覆盖。当GC压缩堆时被移动的一个。这是最有可能的结果。
  • 它被添加到堆段的空闲列表中,准备好由下一个分配使用。通常在将它与块之前和之后的可用空间或分配合并之后,如果它们也被释放,这是很常见的。
  • 这是堆段中剩余的最后一个分配。该段可能被添加到未使用的段池中,当需要新的gen#0段时,可以重新使用该段。或者它的地址空间可能会返回给操作系统。

大对象堆有点不同,第一颗子弹不适用。请注意,"内存位置的心理模型已被删除"不是很准确。您将获得更多的内存位置将被重复使用"。堆栈大致相同,没有活动"删除堆栈帧"码。它被遗忘了,迟早会被覆盖。几乎总是更快,微秒。

答案 2 :(得分:1)

这是您无法回答的问题,因为它是您执行平台的实现细节。根据平台/版本,答案可能完全不同。您可以考虑的是方法范围,值类型与引用类型以及字符串是特殊的。