这是真的 - 垃圾收集器不会收集struct类型的对象

时间:2012-07-28 09:12:18

标签: c# asp.net .net oop garbage-collection

昨天,我们讨论了Gargbage收集问题。

讨论了使用Classes创建的对象是由垃圾收集器收集的,但如果使用struct

创建它,则GC无法收集它们。

我知道结构使用堆栈,而类使用堆。

但是,我猜GC从不收集非托管代码。这是否意味着Structure类型是非托管代码。 (我不这么认为)。

或者是GC只处理Heap而不是Stack?

如果是,那么int数据类型呢。 int是struct而不是class。因此,如果我们定义int类型的对象,是不是由GC管理?

4 个答案:

答案 0 :(得分:6)

如果无法从GC根目录访问,GC将收集任何托管对象(以及Structures 托管对象)。

  

但如果使用struct创建它,则无法收集它。

你被告知的是不正确的。如何创建托管对象无关紧要 - 如果没有对它的引用,它将最终被收集。

  

OR是否意味着GC只处理Heap而不是Stack?

GC处理对象图 - 如果任何GC根可以访问对象,则不会收集它们,如果不是,则最终会收集它们。堆栈和堆不相关。

  

因此,如果我们定义int类型的对象,是不是由GC管理?

int(AKA System.Int32)是一个托管对象 - 一个结构。如果您在某个类中声明了int字段,并且该类超出了范围,则最终会由GC收集int


作为@leppie commented,在许多情况下,结构将被放置在堆栈上,当堆栈弹出时它们将不再存在 - 在这种情况下,GC不会被涉及(和不需要)。

答案 1 :(得分:2)

  

如果使用struct创建它,则无法收集它。

不正确。如果它没有被引用,它将最终被收集。

  

我知道结构使用堆栈,类使用堆。

这是一种常见的误解。请参阅this Lippert's article for details

我认为你引用的想法是通常 GC不会收集堆栈上的任何数据,因为一旦程序执行离开其范围,堆栈就会被销毁。这意味着任何直接放入堆栈的数据(可能意味着值类型而应该意味着对所有其他数据的引用)将自动清除,无需使用GC 。 GC的工作是独立于其(数据)类型清除数据。如果它没有被引用 - 它就被收集了。

答案 2 :(得分:1)

  

但是,我猜GC从不收集非托管代码。这就是说结构类型是非托管代码。 (我不这么认为)。

我不明白你在这里问的是什么。

  

OR是否意味着GC只处理Heap而不是Stack?

是和否.GC负责处理引用类型实例所需的内存(始终在托管堆上创建)。您可以将“堆栈”视为与当前执行线程关联的一块内存。堆栈可以包含用于引用托管堆上分配的类型的句柄。在这种情况下,GC“关心”:它不会从这些实例的托管堆中收集内存,直到堆栈上的这些引用存在。堆栈还可以包含值类型的实例(不是对!的引用),在这种情况下GC不关心...

  

如果是,那么int数据类型呢。 int是struct而不是class。因此,如果我们定义int类型的对象,是不是由GC管理?

这个问题有点误导。假设您在堆栈中“分配”int的实例:

void Foo()
{
    // ...
    int tTmp;
    //...
}

在这种情况下,GC不关心tTmp。如果它超出范围,它将被放置在当前线程的堆栈中并被删除。但如果你这样做:

void Foo()
{
    //...
    var tTmp = new int [] {
        1, 2, 3, 4
    };
    //...
}

然后在托管堆上创建4个整数的数组,GC负责tTmp。它还“间接地”处理数组内容所需的内存,这恰好是四个整数所需的空间......

答案 3 :(得分:0)

让我们看看 .NET标准(ECMA-334)

  

值类型与值的变量中的引用类型不同   类型直接包含它们的数据,而引用的变量   types存储对其数据的引用,后者称为   对象。对于引用类型,可以有两个变量   引用相同的对象,因此可以对其进行操作   变量以影响另一个变量引用的对象。同   值类型,每个变量都有自己的数据副本,以及   对一方的操作不可能影响另一方。

换句话说,垃圾收集器没有理由关心值类型,因为它们自己清理后(当它们超出范围时),它们包含自己的数据。 GC用于清理共享(引用)数据。

请注意,不仅结构是值类型:

  

值类型是结构类型或枚举类型。 C#   提供了一组称为简单类型的预定义结构类型。该   简单类型通过保留字识别。

所以,例如“int”类型是所谓的“简单类型”,值类型。它与其他结构有点不同,因为像+ - * /这样的操作最终可能被编译成基本操作,而不是函数调用。