.NET中栈和堆的比较1

原文出处:
http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory01122006130034PM/csharp_memory.aspx
尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(Garbage Collection),但是我们还是应该了解它们,以优化我们的应用程序。同时,还需要具备一些基础的内存管理工作机制的知识,这样能够有助于解释我们日常程序编写中的变量的行为。在本文中我将讲解栈和堆的基本知识,变量类型以及为什么一些变量能够按照它们自己的方式工作。

在.NET framework环境下,当我们的代码执行时,内存中有两个地方用来存储这些代码。假如你不曾了解,那就让我来给你介绍栈(Stack)和堆(Heap)。栈和堆都用来帮助我们运行代码的,它们驻留在机器内存中,且包含所有代码执行所需要的信息。

* 栈vs堆:有什么不同?

栈负责保存我们的代码执行(或调用)路径,而堆则负责保存对象(或者说数据,接下来将谈到很多关于堆的问题)的路径。

可以将栈想象成一堆从顶向下堆叠的盒子。当每调用一次方法时,我们将应用程序中所要发生的事情记录在栈顶的一个盒子中,而我们每次只能够使用栈顶的那个盒子。当我们栈顶的盒子被使用完之后,或者说方法执行完毕之后,我们将抛开这个盒子然后继续使用栈顶上的新盒子。堆的工作原理比较相似,但大多数时候堆用作保存信息而非保存执行路径,因此堆能够在任意时间被访问。与栈相比堆没有任何访问限制,堆就像床上的旧衣服,我们并没有花时间去整理,那是因为可以随时找到一件我们需要的衣服,而栈就像储物柜里堆叠的鞋盒,我们只能从最顶层的盒子开始取,直到发现那只合适的。

[heapvsstack1.gif]

以上图片并不是内存中真实的表现形式,但能够帮助我们区分栈和堆。

栈是自行维护的,也就是说内存自动维护栈,当栈顶的盒子不再被使用,它将被抛出。相反的,堆需要考虑垃圾回收,垃圾回收用于保持堆的整洁性,没有人愿意看到周围都是赃衣服,那简直太臭了!

* 栈和堆里有些什么?

当我们的代码执行的时候,栈和堆中主要放置了四种类型的数据:值类型(Value Type),引用类型(Reference Type),指针(Pointer),指令(Instruction)。

1.值类型:

在C#中,所有被声明为以下类型的事物被称为值类型:

bool
byte
char
decimal
double
enum
float
int
long
sbyte
short
struct
uint
ulong
ushort

2.引用类型:

所有的被声明为以下类型的事物被称为引用类型:

class
interface
delegate
object
string

3.指针:

在内存管理方案中放置的第三种类型是类型引用,引用通常就是一个指针。我们不会显示的使用指针,它们由公共语言运行时(CLR)来管理。指针(或引用)是不同于引用类型的,是因为当我们说某个事物是一个引用类型时就意味着我们是通过指针来访问它的。指针是一块内存空间,而它指向另一个内存空间。就像栈和堆一样,指针也同样要占用内存空间,但它的值是一个内存地址或者为空。

[heapvsstack2.gif]

4.指令:

在后面的文章中你会看到指令是如何工作的...

* 如何决定放哪儿?

这里有一条黄金规则:

1. 引用类型总是放在堆中。(够简单的吧?)

2. 值类型和指针总是放在它们被声明的地方。(这条稍微复杂点,需要知道栈是如何工作的,然后才能断定是在哪儿被声明的。)

就像我们先前提到的,栈是负责保存我们的代码执行(或调用)时的路径。当我们的代码开始调用一个方法时,将放置一段编码指令(在方法中)到栈上,紧接着放置方法的参数,然后代码执行到方法中的被“压栈”至栈顶的变量位置。通过以下例子很容易理解...

下面是一个方法(Method):

           public int AddFive(int pValue)
          {
                int result;
                result = pValue + 5;
                return result;
          }

现在就来看看在栈顶发生了些什么,记住我们所观察的栈顶下实际已经压入了许多别的内容。

首先方法(只包含需要执行的逻辑字节,即执行该方法的指令,而非方法体内的数据)入栈,紧接着是方法的参数入栈。(我们将在后面讨论更多的参数传递)

[heapvsstack3.gif]

接着,控制(即执行方法的线程)被传递到堆栈中AddFive()的指令上,

[heapvsstack4.gif]

当方法执行时,我们需要在栈上为“result”变量分配一些内存,

[heapvsstack5.gif]

The method finishes execution and our result is returned.
方法执行完成,然后方法的结果被返回。

[heapvsstack6.gif]

通过将栈指针指向AddFive()方法曾使用的可用的内存地址,所有在栈上的该方法所使用内存都被清空,且程序将自动回到栈上最初的方法调用的位置(在本例中不会看到)。

[heapvsstack7.gif]

在这个例子中,我们的"result"变量是被放置在栈上的,事实上,当值类型数据在方法体中被声明时,它们都是被放置在栈上的。

值类型数据有时也被放置在堆上。记住这条规则--值类型总是放在它们被声明的地方。好的,如果一个值类型数据在方法体外被声明,且存在于一个引用类型中,那么它将被堆中的引用类型所取代。

来看另一个例子:

假如我们有这样一个MyInt类(它是引用类型因为它是一个类类型):

          public class MyInt
          {         
             public int MyValue;
          }

然后执行下面的方法:

          public MyInt AddFive(int pValue)
          {
                MyInt result = new MyInt();
                result.MyValue = pValue + 5;
                return result;
          }

就像前面提到的,方法及方法的参数被放置到栈上,接下来,控制被传递到堆栈中AddFive()的指令上。

[heapvsstack8.gif]

接着会出现一些有趣的现象...

因为"MyInt"是一个引用类型,它将被放置在堆上,同时在栈上生成一个指向这个堆的指针引用。

[heapvsstack9.gif]

在AddFive()方法被执行之后,我们将清空...

[heapvsstack10.gif]

我们将剩下孤独的MyInt对象在堆中(栈中将不会存在任何指向MyInt对象的指针!)

[heapvsstack11.gif]

这就是垃圾回收器(后简称GC)起作用的地方。当我们的程序达到了一个特定的内存阀值,我们需要更多的堆空间的时候,GC开始起作用。GC将停止所有正在运行的线程,找出在堆中存在的所有不再被主程序访问的对象,并删除它们。然后GC会重新组织堆中所有剩下的对象来节省空间,并调整栈和堆中所有与这些对象相关的指针。你肯定会想到这个过程非常耗费性能,所以这时你就会知道为什么我们需要如此重视栈和堆里有些什么,特别是在需要编写高性能的代码时。

Ok... 这太棒了, 当它是如何影响我的?

Good question.

当我们使用引用类型时,我们实际是在处理该类型的指针,而非该类型本身。当我们使用值类型时,我们是在使用值类型本身。听起来很迷糊吧?

同样,例子是最好的描述。

假如我们执行以下的方法:

          public int ReturnValue()
          {
                int x = new int();
                x = 3;
                int y = new int();
                y = x;     
                y = 4;         
                return x;
          }

我们将得到值3,很简单,对吧?

假如我们首先使用MyInt类

     public class MyInt
          {
                public int MyValue;
          }

接着执行以下的方法:

          public int ReturnValue2()
          {
                MyInt x = new MyInt();
                x.MyValue = 3;
                MyInt y = new MyInt();
                y = x;                
                y.MyValue = 4;             
                return x.MyValue;
          }

我们将得到什么?...    4!

为什么?...  x.MyValue怎么会变成4了呢?...  看看我们所做的然后就知道是怎么回事了:

在第一例子中,一切都像计划的那样进行着:

          public int ReturnValue()
          {
                int x = 3;
                int y = x;   
                y = 4;
                return x;
          }

[heapvsstack12.gif]

在第二个例子中,我们没有得到"3"是因为变量"x"和"y"都同时指向了堆中相同的对象。
          public int ReturnValue2()
          {
                MyInt x;
                x.MyValue = 3;
                MyInt y;
                y = x;               
                y.MyValue = 4;
                return x.MyValue;
          }

[heapvsstack13.gif]

希望以上内容能够使你对C#中的值类型和引用类型的基本区别有一个更好的认识,并且对指针及指针是何时被使用的有一定的基本了解。在系列的下一个部分,我们将深入内存管理并专门讨论方法参数。

To be continued...

转载于:https://www.cnblogs.com/tager/archive/2009/03/27/1422899.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423492.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端学习(1):HTML和CSS导学

最近为什么捡起前端,主要工作太忙,有时间就会抓一下后端,前端是我以前啃得比较多的 再来一次呢,工作在忙也不能停止学习勒 第一部分 第二部分 第三部分 第四部分 如何学习

Spring Boot----Dubbo原理分析

环境:需要创建一个dubbo.xml 通过ImportResource()导入xml: 1、首先spring启动解析配置文件的每一个标签的总接口是 org.springframework.beans.factory.xml.BeanDefinitionParser 2、DubboBeanDefinitionParser是它的一个实现类,通过调用par…

hive中order by,sort by, distribute by, cluster by作用以及用法

1. order by Hive中的order by跟传统的sql语言中的order by作用是一样的,会对查询的结果做一次全局排序,所以说,只有hive的sql中制定了order by所有的数据都会到同一个reducer进行处理(不管有多少map,也不管文件有多少…

最有价值的100句话

1:能不抽烟最好不抽,它或许可以帮助你吸引一些女生,但不抽绝不会招来厌烦,表现男子气概的途径有很多,没必要拿健康做赌注。    2:给自己定目标,一年,两年,五年,也许你…

前端学习(2):什么是html和css

什么是HTML? W3C:万维网联盟,是目前web技术领域最具权威和影响力的标准机构,目前为止,W3C已发布了200多项影响深远的web技术标准及实施指南。 Hypertext markup language:超文本标记语言,该语言书写的代码通…

基于小程序·云开发构建高考查分小程序丨实战

2019高考报名人数达到了 1031 万的新高,作为一名三年前参考高考的准程序猿,赶在高考前,加班加点从零开始做了一款高考查分小程序,算是一名老学长送给学弟学妹们的高考礼。上线仅 1 个月,用户数就突破了 1k,…

浅谈 DML、DDL、DCL的区别

一、DML DML(data manipulation language)数据操纵语言:就是我们最经常用到的 SELECT、UPDATE、INSERT、DELETE。 主要用来对数据库的数据进行一些操作。 SELECT 列名称 FROM 表名称 UPDATE 表名称 SET 列名称 新值 WHERE 列名称 某值 IN…

前端学习(3):vs code编辑器

下载地址 https://code.visualstudio.com 下载安装教程 变成中文 在编辑器中运行我们的网页 open in browser view in browser 选中文件----首选项----设置 常用快捷键

QuickPart应用系列

在上一篇解决方案包部署与收回篇章中,我只是稍微提了下QuickPart.也许刚接触这块内容的朋友,可能还不是很清楚,QuickPart具体的功能能实现什么。首先要告诉你的是QuickPart的人性化之处,那就是给开发人员开发webpart提供更简洁的方…

spring----IOC知识点

//可以修改Bean定义的属性(不是修改Bean) Component public class TulingBeanFactoryProcessor implements BeanFactoryPostProcessor {Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {beanFactory.getBean…

Hive分析窗口函数(五) GROUPING SETS,GROUPING__ID,CUBE,ROLLUP

GROUPING SETS 该关键字可以实现同一数据集的多重group by操作。事实上GROUPING SETS是多个GROUP BY进行UNION ALL操作的简单表达,它仅仅使用一个stage完成这些操作。GROUPING SETS的子句中如果包含()数据集,则表示整体聚合。 Aggregate Query with GRO…

前端学习(4):chome浏览器

一、认识浏览器 浏览器是网页显示、运行的平台,常用的浏览器有IE、火狐(Firefox)、谷歌(Chrome)、Safari和Opera等。我们平时称为五大浏览器。IE最新版为Edge。 常用浏览器 二、浏览器市场份额 可以通过百度的统计网…

JS的IE和Firefox兼容性总结

JS的IE和Firefox兼容性汇编(原作:hotman_x) 以下以 IE 代替 Internet Explorer,以 MF 代替 Mozzila Firefox 1. document.form.item 问题 (1)现有问题: 现有代码中存在许多 document.formName.item(&q…

spring----注解

以后想到了在写 1、DependsOn("xx") User bean被创建之前,先创建xx bean; DependsOn("xx") public class User{private int id; }转载于:https://www.cnblogs.com/yanxiaoge/p/11479628.html

Hive分析窗口函数 NTILE,ROW_NUMBER,RANK,DENSE_RANK

本文中介绍前几个序列函数,NTILE,ROW_NUMBER,RANK,DENSE_RANK,下面会一一解释各自的用途。Hive版本为 apache-hive-0.13.1 数据准备: cookie1,2015-04-10,1cookie1,2015-04-11,5cookie1,2015-04-12,7cookie1,2015-04-13,3cookie1,2015-04-14,…

前端学习(5):深入了解网站开发

要了解web前后端的区别,首先必须得清楚什么是web前端和web后端。 首先:web的本意是蜘蛛网和网的意思,在网页设计中我们称为网页的意思。现广泛译作网络、互联网等技术领域。表现为三种形式,即超文本(hypertext)、超媒体(hypermed…

实战 IE8 开发人员工具

今天整理我收藏的漫画的时候发现 风云3 少了两集(486、487),这对于收藏者来说基本是不可忍受的; 从风云一到三,应该一集也不能少的; 决定上网去找找,不过溜达一圈常去的分享论坛,由于…

spring----Bean的生命周期和循环依赖

循环依赖&#xff1a; A类引用了B&#xff0c;B类引用了A&#xff0c;像这种循环着依赖就是循环依赖&#xff1b; 对于这种配置不会报错 <bean id"instanceA" class"com.zy.entities.InstanceA"><property name"instanceB" ref"in…

SQL count和case when配合统计给定条件下不重复的记录数

Iamlaosong文 1、我们知道&#xff0c;SQL语句中用count函数统计记录数量&#xff0c;配合distinct关键字可以统计非重复的记录数量。例如&#xff1a; select count(*), count(city_name), count(distinct city_name) from tb_county 查询结果是&#xff1a; 2534 2534 …

前端学习(6):javascript简介

我们需要思考以下六个问题&#xff1a; 1、javaScript是什么&#xff1f; 2、javaScript的用途是什么&#xff1f; 3、javaScript和ECMAScript的关系是什么&#xff1f; 4、javaScript由哪几部分组成&#xff1f; 5、javaScript的执行原理是怎样的&#xff1f; 6、在页面…