【问题标题】:Where Are Value Types Stored In (C#) Generic Collections值类型存储在 (C#) 泛型集合中的位置
【发布时间】:2011-04-16 23:13:51
【问题描述】:

确实,对于值类型,泛型集合比非泛型集合执行得更好。 (即 List 与 ArrayList)。

但是,除了装箱-拆箱步骤之外,为什么会这样呢?一旦添加到集合中,值类型对象存储在哪里?在非泛型集合中,它们会被装箱并存储在堆上,泛型有什么不同?

【问题讨论】:

  • 对于相同数量的整数,sizeof(List) ~= sizeof(int[]) ~= 1/2 sizeof(ArrayList on x86) ~= 1/3 sizeof(ArrayList on x86-64)。此外,由于装箱,数据的局部性会影响 ArrayList,并且会产生超出必要的内存碎片。

标签: c# .net types generics


【解决方案1】:

相关的实现细节是List<T> 的底层存储是一个T[]。因此,对于List<int>,值将存储在 int[] 中。整数存储在一块连续的内存中,从垃圾收集堆中分配。

它如此之快的原因不仅在于整数没有被装箱,还在于 int[] 与 CPU 缓存配合得非常好。当您读取第一个元素时,您基本上可以免费获得接下来的 15 个元素,而无需读取慢速 RAM 或二级缓存。这对于一个装箱的 int 来说几乎没有那么好,因为它太大了,而且额外的引用可能有很差的缓存局部性。但是,垃圾收集器确实有助于通过压缩堆来降低成本。

【讨论】:

  • 啊,唯一明确表示List<T> 实际上使用数组作为后备存储的答案(而且说得好)。 List<T> 只是一个数组的包装器,它添加了对调整大小的支持,实际上几乎所有的集合类型都归结为数组,除了特殊的集合,例如 LinkedList<T>
  • 所以ArrayList 具有双重性能打击。首先,您必须从数组中取消引用该值以获取对象(装箱值类型),然后您必须将其拆箱。
  • 但是如果ArrayList 的后备存储是object[],那么首先您必须在数组中查找该项目,它会为您提供对包含int 的对象的引用。您必须取消引用才能获取对象,然后您必须将其拆箱。将其与 List 进行比较,您所要做的就是从 int[] 后备商店中获取项目。
  • @Jim,我认为你是对的,修改后的帖子指出了取消引用的额外成本。
【解决方案2】:

与存储在非泛型等效项中的值类型相比,泛型的性能提升通常仅与泛型使用的值类型有关。

这是因为使用泛型值类型不需要强制转换为对象并存储在堆上(装箱)。事实上,它们可以保留在性能更高的堆栈上。

http://msdn.microsoft.com/en-us/library/ms172181.aspx

【讨论】:

    【解决方案3】:

    ArrayList 是对存储在堆中的对象的本地引用数组。

    引用类型的通用列表是对存储在堆中的对象的本地引用数组。

    值类型的通用列表是这些值类型的本地数组。

    有两个内存区域,大多数引用称为“堆栈”和“堆”。大多数使用这些术语的人不知道为什么。 (“堆栈”可能是堆栈,但堆几乎可以肯定不是堆)。我更喜欢“这里”和“那里”这两个词。装箱时,值类型数据存储在“那边”。当存储在数组中(可能在通用列表中)时,值类型数据存储在“这里”。 “这边”比较好。

    【讨论】:

    • “这里” - “那里”???什么????什么是“本地数组”?这个答案比什么都更令人困惑。
    • @0xA3:本地数组是存储在“堆栈”中的数组。是不是这样写更有启发性?
    • 好的,那么您的说法不正确(如果您指的是常用意义上的堆栈)。 ArrayListList<T> 都使用 System.Array 作为内部存储,它是一种引用类型并存储在“那里”。
    • 嗯。我认为答案很清楚。我认为这最后一条评论确切地说明了为什么使用术语“堆”和“堆栈”会令人困惑。确实,数组将被分配在“堆”上,但真正的问题是分配给存储数组每个元素的内存在哪里?在List<T> 中,存储值类型的内存位于为 System.Array 分配的内存中(即“Over Here”)。在 ArrayList 中,每个元素只是对装箱值类型的引用,因此存储每个值类型的实际内存在“堆”的其他地方,即“那边”的某个地方。
    【解决方案4】:

    除了装箱和拆箱之外,还有几个原因,包括内存缓存和枚举它们以执行其工作的方式。查看this post, especially the comments

    【讨论】:

      【解决方案5】:

      在泛型中,例如List<T>,它们仍然存储在堆中。不同之处在于,在内部,List<int> 生成了一个整数数组,并且可以直接存储数字。使用 ArrayList,您最终会存储一个对装箱整数值的引用数组。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-03
        • 2012-08-05
        相关资源
        最近更新 更多