【问题标题】:When happens to value types when they're removed from a collection?值类型从集合中移除时会发生什么情况?
【发布时间】:2010-12-04 01:27:06
【问题描述】:

假设我有一些像这样的简单struct

public struct WeightedInt {
    public int value;
    public double weight;
}

那么假设我有一个这种结构的实例集合:

List<WeightedInt> weightedInts = new List<WeightedInt>();

据我了解,值类型与引用类型相比,值类型是在堆栈上分配的,因此一旦实例化所述对象的函数终止,值类型对象就会从内存中清除。这意味着在以下代码中:

void AddWeightedIntToList(int value, double weight) {
    WeightedInt wint = new WeightedInt();
    wint.value = value;
    wint.weight = weight;

    weightedInts.Add(wint);
}

局部变量wint副本被添加到weightedInts,而局部变量自身AddWeightedIntToList完成后从内存中删除。 p>

首先:这是正确的吗?

其次,wint 的这个副本存储在哪里?它不能在堆栈上,因为一旦函数完成它就会消失(对吗?)。这是否意味着副本与weightedInts 一起存储在堆上?是不是被移除后被垃圾回收了,就好像它是引用类型的一个实例一样?

这个问题肯定有可能在某篇文章中得到解答,在这种情况下,指向该文章的链接将是一个完全可以接受的答案。我只是没有运气找到它。

【问题讨论】:

  • 值类型不在堆栈上分配。不在迭代器块中或被匿名函数关闭的值类型的局部变量可以在堆栈上分配,并且经常在堆栈上分配,但它们需要不是。它们可以在堆上分配,也可以在寄存器中分配。
  • 如果您对“值类型分配在堆栈上”的错误分析感兴趣,这里有两篇我写过的关于该主题的文章:blogs.msdn.com/ericlippert/archive/2009/04/27/…blogs.msdn.com/ericlippert/archive/2009/05/04/…跨度>

标签: c# .net garbage-collection heap-memory stack-memory


【解决方案1】:

首先:这是正确的吗?

是的。一旦作用域结束,原件就“消失”了。

其次,这个 wint 的副本存储在哪里?它不能在堆栈上,因为一旦函数完成它就会消失(对吗?)。这是否意味着副本与 weightedInts 一起存储在堆上?是不是被移除后被垃圾回收了,就好像它是引用类型的一个实例一样?

List&lt;WeightedInt&gt; 的实例在堆上创建一个数组。当您将该数组的一部分“添加”到列表中时,您正在为该数组的一部分分配您的值类型的副本。该值保存在堆上,作为数组的一部分(List 类的内部)。

当您的 weightedInts 成员超出范围时,它将变得无根,并且有资格被垃圾收集。在此之后的某个时间点,GC 将运行,并释放与其内部数组关联的内存,从而释放与您的 wint 副本关联的内存。


编辑:

另外,当你打电话时:

weightedInts.Remove(wint);

发生了一些事情(List&lt;T&gt;)。

首先,列表找到等于 wint 的值类型的第一个实例的索引。然后它调用 RemoteAt(index)。

RemoveAt(index) 方法基本上将内部大小标记为小一,然后检查您要删除的索引。如果它在列表的中间,它实际上使用 Array.Copy 将所有值类型实例复制到一个元素上,以“缩小”列表。然后它将数组末尾的内存清零。

数组本身不会缩小,因此删除元素不会释放内存。如果你想回收这块内存(甚至让它有资格被 GC 释放),你需要调用List&lt;T&gt;.TrimExcess()。

【讨论】:

  • 根据您的说法,我可以得出结论,一旦weightedInts 超出范围,添加到weightedInts 的值将被垃圾收集。但是我仍然不清楚的是,如果我打电话给weightedInts.Remove(wint);,它会发生什么——它会一直躺在那里直到weightedInts 被垃圾收集吗?
  • 所以一个整数列表将存储在堆中,堆栈上的唯一分配将是指向数组堆的指针?
  • 嗯,它实际上是一个指向 List 的指针,它在内部包含一个指向数组的指针和一个用于跟踪的整数值,但是是的,这就是一般的想法。数组在堆上分配,List. 中的数组引用也是如此
  • @Dan,您的 weightedInts 在超出范围时不会被垃圾收集,而是在超出范围后在某个时间进行垃圾收集。 (在大多数情况下没有区别)
  • @Reed 首先非常好的答案。但严格来说,我认为它不是指向存储在堆上的 List 的指针,它是一个引用(在 MS 实现中,它们基于我记得的指针,但它们不需要也不需要是)
【解决方案2】:

值类型总是在栈上分配是一个常见的误解。

您刚刚展示的示例是在堆上分配值类型的完美示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-19
    • 2013-08-04
    • 2014-02-17
    相关资源
    最近更新 更多