Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 引用类型位于堆上,值类型位于堆栈上_C# - Fatal编程技术网

C# 引用类型位于堆上,值类型位于堆栈上

C# 引用类型位于堆上,值类型位于堆栈上,c#,C#,在阅读“C#indepth”时,我浏览了“引用类型位于堆上,值类型位于堆栈上”一节 现在我能理解的是(主要是ref类型): 我只是想澄清一下我的假设是否正确。谢谢 编辑: 我应该使用diff变量,我认为这造成了混乱。所以我修改了代码 编辑: 是的,正如乔恩提到的-这是一个神话,我应该提到。我的道歉。 整个“堆上的引用类型,堆栈上的值类型”不仅是一种不好的看待它的方式,但是它也错了。c留在堆栈上,因为至少是一个值类型,而a在托管堆中,因为是引用类型的字段我可能是一个有点有用的抽象概念,可以在脑海中

在阅读“C#indepth”时,我浏览了“引用类型位于堆上,值类型位于堆栈上”一节

现在我能理解的是(主要是ref类型):

我只是想澄清一下我的假设是否正确。谢谢 编辑: 我应该使用diff变量,我认为这造成了混乱。所以我修改了代码

编辑: 是的,正如乔恩提到的-这是一个神话,我应该提到。我的道歉。


整个“堆上的引用类型,堆栈上的值类型”不仅是一种不好的看待它的方式,但是它也错了。

c
留在堆栈上,因为至少是一个值类型,而
a
在托管堆中,因为是引用类型的字段

我可能是一个有点有用的抽象概念,可以在脑海中了解幕后发生的事情。但在任何当前发布的JIT编译器版本中都不是这样。这可能是问题的症结所在,实际的分配位置是JIT编译器实现的细节

至少有六个地方的值类型值可以与主流(x86和x64)抖动共存:

  • 在堆栈帧中,通过局部变量声明或方法调用将
  • 在CPU寄存器中,JIT在发布构建中执行的一种非常常见的优化。用于将参数传递给方法,前两个x86,四个用于x64。和局部变量(如果可能)
  • 在FPU堆栈上,x86抖动用于浮点值
  • 在GC堆上,当值是引用类型的一部分时
  • 在AppDomain的加载程序堆上,当变量声明为静态时
  • 当变量具有[ThreadStatic]属性时,在线程本地存储中
引用类型对象通常在GC堆上分配。但我知道一个特殊的例外,源代码中的文本生成的插入字符串被分配到AppDomain的加载程序堆中。这完全像运行时的对象,只是它没有链接到GC堆,收集器根本看不到它

寻址您的代码段:

  • 是的,“a”很可能存储在GG堆上
  • “x”总是在x86和x64上的CPU寄存器中传递。“y”将位于x64上的CPU寄存器中,x86上的堆栈
  • “c”很可能根本不存在,因为代码无效,所以被JIT编译器删除

引用类型的存储位置(变量、字段、数组元素等)保存对堆上对象的引用;原语值类型的存储位置将其值保存在其内部;结构类型的存储位置保存其所有字段,每个字段可能是引用或值类型。如果一个类实例包含两个不同的非空字符串,一个点和一个整数,那么该点的X和Y坐标以及独立整数和对这两个字符串的引用都将保存在一个堆对象中。每个字符串将保存在不同的堆对象中。关于类与结构的存储位置的关键点是,除了类实体持有对自身的引用外,类或结构中的每个非空引用类型字段将持有对堆上其他对象的引用。

用C/C++的术语来考虑它

每当你在堆上创建一个“新”的东西,或者使用malloc,也就是说,“对象”在堆上,指针本身就被放在它所属的结构(或函数,实际上只是另一个结构)范围内的堆栈上。如果它是一个局部变量或引用类型(指针),它将进入堆栈


换句话说,引用类型指向的>对象<位于堆上,而堆栈上的指针本身就是指针。当程序将指针从堆栈中弹出时,会发生内存泄漏,但堆中的内存尚未释放以供使用——如果对其位置的引用丢失,您如何知道要释放哪些内存?好吧,C/C++不行,在引用从堆栈中弹出并永远丢失之前,您必须自己做,但这就是现代语言以其奇特的“垃圾收集堆”出现的原因。与隐式清理任何分配的堆内存相比,显式清理任何已分配的堆内存更可取,因为这样做“更便宜”(就CPU资源而言)。

引用Jon Skeet的话,说明引用和值类型在.Net应用程序中的存储方式和位置:

变量的内存插槽存储在堆栈或 堆它取决于声明它的上下文:

  • 每个局部变量(即方法中声明的一个)都存储在堆栈上。这包括引用类型变量-变量本身是 但请记住,引用类型变量的值 只是一个引用(或null),而不是对象本身。方法 参数也算作局部变量,但如果用 ref修饰符,它们没有自己的插槽,但与共享一个插槽 调用代码中使用的变量。请参阅我关于参数的文章 我想知道更多的细节
  • 引用类型的实例变量始终位于堆上。这就是物体本身“存在”的地方
  • 值类型的实例变量存储在与声明值类型的变量相同的上下文中。用于存储的内存插槽 实例中有效地包含每个字段的插槽 例如。这意味着(给定前两点)结构 方法中声明的变量将始终位于堆栈上,而 作为类的实例字段的结构变量将位于 堆
  • 每个静态变量都存储在堆上,不管它是否在引用中声明
    class Program
    {
        int a = 5;  // stored in heap
    
        public void Add(int x, int y) // x,y stored in stack
        {
            int c = x + y;  // c  stored in stack
        }
    }