在C#中销毁struct对象?

我有点困惑的是,在C#中只有引用类型被垃圾收集。 这意味着GC只选择内存解除分配的引用类型。 那么值类型会发生什么呢?因为它们也会占用堆栈上的内存?

首先,它们是在堆栈还是堆的一部分取决于它们所属的上下文 – 如果它们在引用类型中,它们无论如何都会在堆上。 (无论如何,你应该考虑你真正关心堆栈/堆分裂的程度 – 正如Eric Lippert写的那样,它主要是一个实现细节 。)

但是,回收上下文时基本上会回收值类型内存 – 因此当您从方法返回时弹出堆栈时,“回收”整个堆栈帧。 同样,如果值类型值实际上是对象的一部分,则在对象进行垃圾回收时回收内存。

简短的回答是你不必担心它:)(这假设除了内存之外你没有其他任何事情需要担心,当然 – 如果你有结构引用需要释放的本机句柄,这是一个有点不同的情况。)

我有点困惑的是,在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分配它,因此它负责清理它。

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

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

此问题中使用的动词太多,如已销毁,已回收,已取消分配,已删除。 这与实际发生的情况不符。 一个局部变量根本不再是挪威鹦鹉风格 。

一个方法只有一个入口点,首先发生的事情是调整CPU堆栈指针。 创建“堆栈框架”,为局部变量创建存储空间。 CLR保证将此空间初始化为0,而不是由于明确的赋值规则而在C#中强烈使用的function。

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

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

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

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

如果在执行后删除堆栈帧,我会假设将取消分配值类型

还要添加堆栈处于线程级别,堆处于应用程序域级别。

因此,当一个线程结束时,它将重新启动该特定线程使用的堆栈内存。

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