我对C#中只有引用类型收集垃圾这一事实感到有点困惑。 这意味着GC只选择内存解除分配的引用类型。 那么值类型会发生什么呢,因为它们也会占用堆栈上的内存?
答案 0 :(得分:30)
首先,它们是在堆栈还是堆的一部分取决于它们所属的上下文 - 如果它们在引用类型中,它们无论如何都会在堆上。 (你应该考虑到你真正关心堆栈/堆分的程度 - 正如Eric Lippert写的那样,它是largely an implementation detail。)
但是,回收上下文时基本上会回收值类型内存 - 因此当您从方法返回时弹出堆栈时,“回收”整个堆栈帧。同样,如果值类型值实际上是对象的一部分,那么当该对象被垃圾回收时,将回收内存。
简短的回答是你不必担心它:)(这假设你没有任何东西其他而不是内存担心,当然 - 如果你已经得到了结构,引用了需要释放的本机句柄,这是一个有点不同的场景。)
答案 1 :(得分:18)
我对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分配了它,因此它负责清理它。
那么值类型会发生什么,因为它们也占用堆栈上的内存?
他们什么都没发生。没有什么需要发生在他们身上。堆栈是一百万字节。堆栈的大小是在线程启动时确定的;它从一百万字节开始,并且在整个线程执行期间保持一百万字节。堆栈中的内存既不会被创建也不会被破坏;只是它的内容被改变了。
答案 2 :(得分:7)
此问题中使用的动词太多,如已销毁,已回收,已取消分配,已删除。这与实际发生的情况不符。局部变量不再是Norwegian parrot style。
一个方法只有一个入口点,首先发生的事情是调整CPU堆栈指针。创建“堆栈框架”,为局部变量创建存储空间。 CLR保证将此空间初始化为0,而不是由于明确的赋值规则而在C#中强烈使用的功能。
即使方法代码中包含多个return
语句,方法也只有一个退出点。此时,堆栈指针简单地恢复到其原始值。实际上它“忘记”了那里的局部变量。它们的值不以任何方式“擦除”,字节仍然存在。但它们不会持续很长时间,程序中的下一个调用将再次覆盖它们。 CLR零初始化规则确保您永远不会观察那些不安全的旧值。
非常非常快,只需一个处理器周期。 C#语言中此行为的可见副作用是值类型不能具有终结器。确保不需要做额外的工作。
答案 3 :(得分:3)
当堆栈超出范围时,堆栈中的值类型将从堆栈中删除。
答案 4 :(得分:3)
值类型一旦超出范围就会被销毁。
答案 5 :(得分:0)
值将被取消分配
答案 6 :(得分:0)
还要添加堆栈处于线程级别,堆处于应用程序域级别。
因此,当一个线程结束时,它将重新记录该特定线程使用的堆栈内存。
答案 7 :(得分:0)
.NET中的每个值类型实例都是其他内容的一部分,可以是更大的封闭值类型实例,堆对象或堆栈帧。每当这些东西出现时,其中的任何结构也会形成;只要包含它们的东西确实存在,那么这些结构将继续存在。当包含结构的东西不再存在时,结构也将如此。没有破坏容器就无法破坏结构,并且没有办法破坏包含一个或多个结构的东西而不破坏其中包含的结构。