2010-01-27 106 views
19

我对C#中只有引用类型被垃圾收集的事实感到有点困惑。 这意味着GC仅挑选存储器取消分配的参考类型。 那么值类型会发生什么,因为它们也会占用堆栈中的内存?在C#中销毁结构对象?

回答

29

一开始,无论他们是堆的堆或部分依赖于什么情况下他们的一部分 - 如果他们是引用类型之内,他们将在堆上反正。 (你应该考虑多少你真的关心栈/堆鸿沟无论如何 - 作为埃里克利珀写,这是largely an implementation detail

然而,当上下文被收回基本值类型的内存被回收 - 所以即使堆栈你从方法返回时弹出,“回收”整个堆栈帧。同样,如果值类型值实际上是对象的一部分,那么当对象被垃圾收集时,内存将被回收。

简短的回答是,你不必担心它:)(这里假设你没有任何东西其他比内存担心,当然 - 如果你已经有了引用的结构对需要释放原生手柄,这是一个有些不同的情景。)当堆栈帧被删除,已执行后,我会承担

+0

感谢您参考此综合性文章。我发现Eric Lippert的全局指令与整个c#哲学非常一致:“只关注语义,我们会为你处理其余的问题” – 2013-12-10 15:32:34

3

堆栈中的值类型超出范围时将从堆栈中删除。

3

值类型一旦超出范围就会被销毁。

0

还要补充一点,堆在一个线程级别,并且堆在应用程序域级别。

所以,当一个线程结束,将通过RECLAM特定线程使用的堆栈存储器。

7

有在这个问题上使用,比如破碎,回收,重新分配,除去过多的动词。这与实际发生的情况并不相符。局部变量根本不复存在,Norwegian parrot style

的方法,具有一个入口点,即首先发生的事情是,CPU堆栈指针调整。创建一个“堆栈框架”,即本地变量的存储空间。 CLR保证这个空间被初始化为0,而不是因为明确的分配规则而在C#中强烈使用的功能。

的方法有出口的一个点,即使你方法的代码穿插着多个return语句。此时,堆栈指针会简单地恢复到其原始值。实际上,它“忘记”那里的局部变量。它们的值不以任何方式“擦除”,字节仍然存在。但是它们不会持续很长时间,您的程序中的下一次呼叫将会再次覆盖它们。 CLR零初始化规则确保您永远不会观察那些不安全的旧值。

非常非常快,只需要一个处理器周期。这种行为在C#语言中的可见副作用是值类型不能有终结器。确保不需要做额外的工作。

+0

这个局部变量已经不存在了!它已不复存在!这是一个前变量。 :) – 2015-12-05 08:03:08

17

我对C#中只有引用类型被垃圾收集的事实感到困惑。

这不是事实。或者说,这个陈述的真实性或虚假性取决于你的意思是“收集垃圾”。垃圾收集器在收集时肯定会查看值类型;这些值类型可能还活着,并持有到引用类型:

struct S { public string str; } 
... 
S s = default(S); // local variable of value type 
s.str = M(); 

在垃圾收集器运行时,它看起来确实在s,因为它需要确定s.str还活着。

我的建议:澄清精确地您的动词“获取垃圾收集”意味着什么。

GC仅挑选存储器取消分配的参考类型。

同样,这不是事实。假设有的

class C { int x; } 

为整数存储器中的实例将是对垃圾收集堆,并且因此被垃圾收集器回收时C的实例变为无根。

为什么你认为只有引用类型的内存被垃圾收集器释放的假?正确的说法是被垃圾回收器分配为的内存是,垃圾回收器取消分配了,我认为这很有道理。 GC分配它,所以它负责清理它。

那么值类型会发生什么,因为它们也会占用堆栈中的内存?

没有什么事情发生在他们身上。没有什么需要发生在他们身上。该堆栈是一百万字节。线程启动时确定栈的大小;它始于一百万字节,并且在整个线程执行期间它保持一百万字节。堆栈中的内存既不被创建也不被破坏;只有其内容被改变。

+26

“堆栈中的内存既不被创建也不被破坏” - 我建议我们称之为** Lippert内存保存法则**。 :) – 2010-01-27 19:11:26

0

.NET中的每个值类型实例都是其他内容的一部分,可能是更大的封闭值类型实例,堆对象或堆栈帧。每当这些事情产生时,其中的任何结构也会产生;那么只要包含它们的东西确实存在,那些结构就会继续存在。当包含结构的东西不复存在时,结构也会如此。在没有破坏容器的情况下,没有办法摧毁一个结构,并且无法销毁包含一个或多个结构的东西而不破坏其中包含的结构。