c#structs / classes stack / heap control?

时间:2010-01-08 22:36:05

标签: c# stack memory-management heap

所以在c ++中它很容易。你想要在堆上分配任何类/结构,使用new。如果你想在堆栈上使用它,请不要使用new。

在C#中我们总是使用new关键字,并且根据它是结构还是类,它在堆栈或堆上分配(结构转到堆栈,堆到类中) - 在某些应用程序中在更改设计时可能会产生巨大的性能差异,只有那些对象才能进入真正属于那里的堆。

我想知道的是 - 有没有直接的方法来控制对象的分配位置,而不管它是声明为struct还是class?我知道值类型(结构)可以装箱去堆(但装箱/拆箱会带来性能成本)。有没有办法在堆栈上分配类?

此外,是否有任何机制来分配原始内存并在C ++中使用类似placement new的内容?我知道这打破了被管理的想法 - 但如果你可以使用你的自定义内存管理,它可以产生很大的性能差异。

我喜欢C#因为它的方便,因为它是垃圾收集器和其他东西 - 但有时候,当处理应用程序的瓶颈时,可能希望能够更好地控制实际发生的事情。

任何提示/提示欢迎:)

编辑:性能示例:

struct Foo1
{
    public int i;
    public float f;
    public double d;
}

struct Foo2
{
   public Foo1[] bar;

   public void Init(){
        bar = new Foo1[100];
        for (int i = 0; i < 100; i++)
            bar[i] = new Foo1();
    }
}

class Program
{
    static void Main(string[] args)
    {
        DateTime time = DateTime.Now;
        Foo2[] arr = new Foo2[1000000];
        for (int i = 0; i < 1000000; i++)
        {
            arr[i] = new Foo2();
            arr[i].Init();
        }

        Console.WriteLine((DateTime.Now - time).TotalMilliseconds);
    }
}

我的机器需要1.8秒才能执行(请注意,实际上只有分配正在进行 - 没有参数传递)

如果Foo1从struct更改为class,则执行需要8.9秒!这慢了五倍

5 个答案:

答案 0 :(得分:9)

虽然在一般情况下确实总是在堆上分配对象,但C#确实允许您下拉到指针级别以获得重型互操作或非常高性能的关键代码。

unsafe块中,您可以使用stackalloc在堆栈上分配对象并将它们用作指针。

引用他们的例子:

// cs_keyword_stackalloc.cs
// compile with: /unsafe
using System; 

class Test
{
   public static unsafe void Main() 
   {
      int* fib = stackalloc int[100];
      int* p = fib;
      *p++ = *p++ = 1;
      for (int i=2; i<100; ++i, ++p)
         *p = p[-1] + p[-2];
      for (int i=0; i<10; ++i)
         Console.WriteLine (fib[i]);
   }
}

但是请注意,您不需要声明整个方法不安全,只需使用unsafe {...}块即可。

答案 1 :(得分:6)

您对值类型与引用类型的位置(堆栈对堆)的解释并不完全正确。

如果结构是引用类型的成员,那么它们也可以在堆上分配。或者,如果您在通过对象引用传递它们时将它们装箱。

您应该阅读http://www.yoda.arachsys.com/csharp/memory.html以更好地了解实际分配不同类型的位置。

另外,在.Net中,你真的不应该关心类型的分配位置 - 正如Eric Lippert所写:the stack is an implementation detail。你最好理解类型传递的语义(通过值,引用等)。

此外,您似乎暗示在堆上分配对象比在堆栈上分配更昂贵。实际上,我认为复制值类型的性能成本超过了在堆栈上稍快分配的任何节省的好处。堆栈和堆栈之间的最大区别在于,在大多数CPU架构中,堆栈更有可能保留在CPU缓存中 - 从而避免缓存未命中。

这不是与有关的最重要问题。您应该决定该类型是否应该具有按值传递的语义。如果没有 - 那么也许它应该是一个参考类型。

答案 2 :(得分:2)

不要被new关键字愚弄,它对结构体是可选的。

在C#中有一个托管世界,你可以享受垃圾收集器和类型安全,而不必(不得不)担心许多内存细节。堆栈/堆差异是无关紧要的,它与复制语义有关。

对于那些您确实需要控制的罕见情况,C#中存在不安全(非托管)部分,具有真实指针和所有内容。

但是C#中的事物成本与C ++中的成本不同,所以不要追捕鬼,不受管理的短暂物品非常便宜。并且编译器可以在堆栈上分配小数组作为优化,您将无法分辨,也不应该关心。

答案 3 :(得分:1)

不要担心 - 你的头脑仍然处于c / c ++世界,在这个世界里,事情发生的地方很重要。 CLR团队中有很多非常聪明的人,整天都在担心这种神奇的做法。

c#中存在一些问题,内存使用通常与意外创建大量微小对象相关联(在循环中执行string = string +其他字符串是经典的)

有一个memprofiler会告诉你发生了什么,如果你真的认为你有内存管理导致的性能问题

我已经在c#中编写了许多性能密集的代码(图形渲染客户端,网络服务器),并且从不必担心任何此类

答案 4 :(得分:1)

这是在C#中查看结构和类的错误方法。在C#中,struct和class之间的区别不在于它的分配位置,而在于复制语义。结构具有值语义,而类具有引用语义。 C ++程序员倾向于阅读更多内容,因为它们习惯于堆栈上具有值语义的对象和具有引用语义的堆上的对象。

如何分配此内存是运行时的实现细节。运行时可以使用堆栈,堆或其喜欢的任何其他混合分配方案。虽然通常结构将被分配在类似堆栈的东西上,并且类将在某种堆上分配,但这不是必需的。例如,一个在函数中分配但未在函数范围之外传递的类可以很容易地在堆栈上分配。