为参考类型分配内存与CLR中的值类型的成本比较是什么?

时间:2017-12-07 12:35:30

标签: c# memory-management struct clr

我经常发现暗示分配对象的代价很​​高。

大多数参考文献都是:

  1. 倾斜,或
  2. 是不完整的,因为他们没有进一步解释什么成本是什么以及如何与任何不是对象的东西进行比较,要么是因为引用是上下文的并且想要得到关于它正在讨论的主题或完全是不诚实的。
  3. 或者说引用遗漏了他们正在比较为对象分配内存的成本。好的,创建一个对象很昂贵,但反对什么?为什么?如何?多少钱?
  4. 我找不到过去遇到的所有引用,但突然发生的事情已经让位于我的动机(以及伴随的挫折)来提出这个问题。它不是一个权威来源,但在这里:

    示例1

    structs上的

    This article说:

      

    使用结构可以避免C#语言中对象的开销。您   可以组合多个字段。这减少了记忆压力。它   (有时)提高了表现。

    现在,我不知道这是在谈论什么。当然,即使是CLR中最简单的对象也会占用12个字节,如下所示:

    1. 4字节,我忘记的东西,我推测某种标题;
    2. 指向对象方法表的指针的4个字节;和
    3. 对象包含的第一个字段的4个字节,即使该对象不包含任何字段或属性。
    4. struct和值类型的布局是否不同?当然,struct也必须有方法表和标题。那么声明struct对象的比较成本是多少?或者就此而言,分配对象与声明任何值类型的成本比较是什么?

      示例2

      考虑另一个例子,其中潜在的动机是避免分配内存以创建新对象。

      在下面的示例中,代码缓存StringBuilder以避免重新创建它,即使对象的所有内容都被删除以替换新内容。它只是创建新对象,即为避免的新对象分配内存。

      来自source of KeyValuePair<TKey, TValue>

      public override string ToString()
      {
          StringBuilder sb = StringBuilderCache.Acquire(0x10);
          sb.Append('[');
          if (this.Key != null)
          {
              sb.Append(this.Key.ToString());
          }
          sb.Append(", ");
          if (this.Value != null)
          {
              sb.Append(this.Value.ToString());
          }
          sb.Append(']');
          return StringBuilderCache.GetStringAndRelease(sb);
      }
      

      请注意代码的第一行调用StringBuilderCache.Acquire。下面是the code for the StringBuilderCache class,其目的是缓存StringBuilder对象的实例,以避免重新创建它。

      internal static class StringBuilderCache
      {
          // Fields
          [ThreadStatic]
          private static StringBuilder CachedInstance;
          private const int MAX_BUILDER_SIZE = 360;
      
          // Methods
          public static StringBuilder Acquire(int capacity = 0x10);
          public static string GetStringAndRelease(StringBuilder sb);
          public static void Release(StringBuilder sb);
      }
      

      以下是此课程的source of the Acquire method。请注意,在清空其内容后,如果以前有实例,它只返回StringBuilder的缓存实例。

      public static StringBuilder Acquire(int capacity = 0x10)
      {
          if (capacity <= 360)
          {
              StringBuilder cachedInstance = CachedInstance;
              if ((cachedInstance != null) && (capacity <= cachedInstance.Capacity))
              {
                  CachedInstance = null;
                  cachedInstance.Clear();
                  return cachedInstance;
              }
          }
          return new StringBuilder(capacity);
      }
      

      示例3

      我刚刚找到一位权威人士暗示上述说法。

      MSDN上的C#编程指南中的Using Structs页面,几乎在文章的最开头,如下所示:

        

      尽管将点表示为class同样方便   自动实现的属性,struct可能在某些方面更有效   场景。例如,如果声明1000个Point对象的数组,   您将为引用每个对象分配额外的内存;在   在这种情况下,struct会更便宜。

      那么,关于创建新对象的这种嘘声是什么呢?

1 个答案:

答案 0 :(得分:-1)

根据我对Polyfun对该问题的评论中链接的答案的解读,我推断新分配新对象所产生的成本差异与为值类型分配内存所产生的成本差异是由于CPU指令数。

当遍历堆栈或在堆栈上分配新内存时,需要通过那么多字节来取消引用堆栈指针,堆上的每个分配都可能涉及遍历堆的大部分,因为堆通常是碎片化的。

我必须承认这对我来说并不是什么新知识,而且在发布这个问题时我似乎忘记了这一点。

然而,我想等一个人为这个问题提供一个更圆满,更有教养的答案。