原文链接:https://jonskeet.uk/csharp/memory.html

  人们在理解值类型和引用类型之间的差异时因为“值类型在栈上分配,引用类型在堆上分配”这句话造成了很多混乱。这完全是不对的,本文试图澄清这个问题。

变量中有什么?

  理解.NET中内存工作方式的关键是理解变量是什么,以及它的值是什么。在最基本的层面上,变量是变量名和内存之间的关联。变量的值是与之关联的内存中的内容。该值占用内存空间的大小和值的解释取决于变量的类型 - 这正是值类型和引用类型之间的差异所在。

  引用类型变量的值始终是引用或null。如果是引用,则它必须是与其变量类型兼容的对象的引用。例如,以Stream s声明的变量s的值是null或Stream类型(或其兼容类型)实例的引用。引用类型变量所占内存空间的大小是引用的大小,引用的大小在32位模式下固定为4个字节,在64位模式下固定为8个字节。

  值类型变量的值始终是其对象本身的值。例如,对于给定的结构:

struct PairOfInts
{
public int a;
public int b;
}

  以PairOfInts pair声明的变量pair的值是整数对本身,而不是对一对整数的引用。其所占内存空间则是两个整数的大小,即8个字节。请注意,值类型变量永远不能赋值为null - 因为这没有任何意义,值类型变量不是一个引用。

那么东西存放在哪里?  

  变量的分配位置取决于声明它的上下文:

  • 局部变量在栈上分配。这包括引用类型变量 - 变量本身位于栈上,其引用的值分配在堆上。方法参数也计为局部变量,但如果使用ref、out、in修饰符修饰它们,则它们不再是原始类型,而是转换为托管指针类型(Type &),此时传递的是原变量的指针,不再是变量本身。
  • 引用类型的对象始终在堆上分配。
  • 值类型的对象始终内联分配。即在方法中声明的值类型变量在栈上分配,而作为类的实例字段的值类型变量将在堆上分配。
  • 静态变量在堆上分配,包括引用类型和值类型中声明的静态变量。无论创建多少个实例,静态变量都共享一个内存空间。

  上述规则有几个例外:在使用匿名方法时的外部变量和迭代器中的局部变量会由编译器优化为其它类型的实例字段,这些变量会转移到堆中分配。

举个例子

  上述文字描述可能听起来有点复杂,但一个完整的例子可以让事情更清楚一些:

using System;

struct PairOfInts
{
static int counter = ; public int a;
public int b; internal PairOfInts(int x, int y)
{
A = x;
B = y;
counter++;
}
} class Test
{
PairOfInts pair;
string name; Test(PairOfInts p, string s, int x)
{
pair = p;
name = s;
pair.a + = x;
} static void Main()
{
PairOfInts z = new PairOfInts(, );
Test t1 = new Test(z, "first", );
Test t2 = new Test(z, "second", );
Test t3 = null;
Test t4 = t1;
//XXX
}
}

  让我们看一下标记“XXX”位置时内存中的内容。

  • 在栈上分配一个PairOfInts类型的对象,对应变量z。
  • 在堆上分配一个Test类型的对象,在栈上分配一个引用指向该对象,对应变量t1。以32位模式举例,该对象在堆中占用20个字节:8个字节的头信息(所有堆对象都有),8个字节用于存储PairOfInts实例,4个字节用于存储字符串引用。
  • 在堆上分配一个Test类型的对象,在栈上分配一个引用指向该对象,对应变量t2。该对象与上面的对象非常相似。
  • 在栈上分配一个引用,对应变量t3。这个引用是null - 它没有引用任何对象。
  • 在栈上分配一个引用,对应变量t4,并赋值t1引用的对象,此时t1和t4引用堆内存中的同一个对象。
  • 最后,在堆内存中有一个静态变量PairOfInts.counter。

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!

作者:Minotauros
出处:https://www.cnblogs.com/minotauros/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[译].Net中的内存的更多相关文章

  1. Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式

    Linux就这个范儿 第15章 七种武器  linux 同步IO: sync.fsync与fdatasync   Linux中的内存大页面huge page/large page  David Cut ...

  2. c++继承中的内存布局

    今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的.看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用. ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C ...

  3. C++继承 派生类中的内存布局(单继承、多继承、虚拟继承)

    今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的.看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用. ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C ...

  4. 如何使用 DBCC MEMORYSTATUS 命令来监视 SQL Server 2005 中的内存使用情况

    https://technet.microsoft.com/en-us/solutionaccelerators/dd537566.aspx 注意:这篇文章是由无人工介入的微软自动的机器翻译软件翻译完 ...

  5. 浅入 .NET Core 中的内存和GC知识

    目录 托管代码 自动内存管理 参考资料: [1]https://docs.microsoft.com/zh-cn/dotnet/standard/managed-code [2]:https://do ...

  6. Java中堆内存和栈内存详解2

    Java中堆内存和栈内存详解   Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...

  7. Unity游戏开发中的内存管理_资料

    内存是手游的硬伤——Unity游戏Mono内存管理及泄漏http://wetest.qq.com/lab/view/135.html 深入浅出再谈Unity内存泄漏http://wetest.qq.c ...

  8. Java中堆内存和栈内存详解

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  9. C++中的内存管理

    在C++中也是少不了对内存的管理,在C++中只要有new的地方,在写代码的时候都要想着delete. new分配的时堆内存,在函数结束的时候不会自动释放,如果不delete我分配的堆内存,则会造成内存 ...

随机推荐

  1. php生成html静态文件

    现在的动态网站存在很多性能上的弊端,seo优化会存在一定的瓶颈,现在将动态的网站代码转换为html静态文件,是浏览器通过html间接的读取动态网站源文件,这对其网站加载速度还是seo优化有着举足轻重的 ...

  2. python+Django 下JWT的使用

    figure:first-child { margin-top: -20px; } #write ol, #write ul { position: relative; } img { max-wid ...

  3. asp.net core 系列之Startup

    这篇文章简单记录 ASP.NET Core中 ,startup类的一些使用. 一.前言 在 Startup类中,一般有两个方法: ConfigureServices 方法: 用来配置应用的 servi ...

  4. ZooKeeper学习之路(二)—— Zookeeper单机环境和集群环境搭建

    一.单机环境搭建 1.1 下载 下载对应版本Zookeeper,这里我下载的版本3.4.14.官方下载地址:https://archive.apache.org/dist/zookeeper/ # w ...

  5. 喵星人教你 HTTP 状态码

    在我们日常 Web 开发中,或多或少的都接触过 HTTP 状态码,那这些状态码代表什么意思呢?熟悉这些状态码又有什么好处呢?下面我就为大家一一道来,可以把本片文章'收藏'以备不时之需. HTTP 状态 ...

  6. 机器学习经典算法之EM

    一.简介 EM 的英文是 Expectation Maximization,所以 EM 算法也叫最大期望算法. 我们先看一个简单的场景:假设你炒了一份菜,想要把它平均分到两个碟子里,该怎么分? 很少有 ...

  7. xshell传送文件

    xshel是一款非常好的ssh远程登入的软件,最近在玩hadoop发现的想把widows上的文件通过xshell直接传送到虚拟机中 这个解决方法还是非常不错的 https://jingyan.baid ...

  8. 驰骋工作流引擎-CCMobile与安卓、IOS集成过程中的问题与解决方案

    CCMobile与安卓.IOS集成过程中的问题与解决方案 前言: CCMobile(2019版本)是CCFlow&JFlow 的一款移动端审批的产品.系统基于mui框架开发,是一款可以兼容An ...

  9. Codeforces Gym101505G:Orchard Division(扫描线+线段树第k大)

    题目链接 题意 给出一个m*m的地图,上面有n个点,现在需要用一个自定义面积的矩形笼罩住恰好n/2个点,并且这个矩形需要有一个点在至少一个角落上,问这个矩形最小的面积是多少. 思路 有点类似于扫描线. ...

  10. c++学习书籍推荐《C++设计新思维》下载

    百度云及其他网盘下载地址:点我 译序by 侯捷 i 译序by 於春景 iii 目录 v 序言by scott meyers xi 序言by john vlissides xv 前言 xvii 致谢 x ...