深入理解内存区域中的堆与栈

空闲着,探讨下堆与栈,这是程序运行中使用的两个内存区域。

能理解C#中每个类型与对象,引用类型与值类型是怎么存储,很多局面性的问题都迎面而开。
为什么呢?现在先举一个例子看下:
字符串不可改变性
string s="Change";
s=s+"String";

如果我们把s输出来,结果为:“ChangeString”;
很多朋友疑问,这不是改变了字符串了吗?
我们不能被他表面给骗了,比方说:
你去工产订制一个变形金刚,但它也是不可变形的,
因为工产里面不能给你生产可变形的金刚,怕你毁灭地球。
这个变形金刚给你怎么DIY都不可以改变,这时你到工产里面再订制另一个变形金刚,然后把旧的那个变形金刚丢垃圾桶里,它已经满足不了你小小的心灵。
这时你手中这个变形金刚变为新的变形金刚,但垃圾桶中的变形金刚改变了没有?还没有吧....唯一变的是成废品。

1,第一个变形金刚是不是给第二个变形金刚替换了?但第一个变形金刚从生产出来到丢掉,它依然没有改变,唯一就是回到垃圾桶去了。
这也是.net中的垃圾回收机制。如果某个引用没有主人了,它将当废品回收。
2.你手上的变形金刚依然是不会变的,因为你是坏人,你指定的变形金刚就是不可变的。


我们再回头看下上面两句话在内存中是怎么声明与存储的:


1.声明一个字符串变量s,把引用指向内存的堆中中的“Change”。
 这样,我们需要输出s时,其实是把s指向的数据“Change”输出来

2.我们把s指向的数据与“string”相加,在字符串中加号相当于合并两字符串。
由于"String"是一个临时的字符串,所以它的引用部分暂时用一个空指定吧。事实上,它一样有一个临时的引用。
这样我们把相加后得到的结果存在堆中“ChangeString”;


3.当我们把“ChangeString”赋给s时,其实是把“ChangeString”所在的引用给了s,然后s原来指向与null指向被垃圾回心机制回收了。
所以这里输出s时,是把s指向的数据输出来。

看了以上的例子,是不是突然明白,为什么字符串是不可改变性?以前百思不得其解,现在一目了然。

注:以下资料出于《C#图解教程》
那好,我们接着来看下程序运行时,在内存区域是怎么来存储数据的。
第一:我们要先了解内存中的两个区域:栈与堆
栈:
栈是一个内存数组,是一个LIFO(last-in first-out,后进先出)的数据结构。
栈存储几种类型数据:
1.某些类型变量的值
2.程序当前执行环境
3.传递给方法的参数
栈的特征:
数据只能从栈的顶端插入或删除;(见图3-6)
把数据放到栈顶称为入栈(push);
从栈顶删除数据称为出栈(pop);

堆:
堆是一块内存区域,在堆里可以分配大块的内存用于存储某类型的数据。与栈不同,堆里的内存可以任意顺序存入和移除。
图3-7展示了一个在堆里放了4项数据的程序。

虽然程序可以在堆里保存数据,但并不能显式地删除它们。CLR的自动GC(Garbage Collector 垃圾收集器)在判断出程序代码将不会再访问某数据项时,自动清除无主的堆对象。
我们因此不可以再操心这项使用其他编程语言时非常容易出错的工作了。图3-8阐明了垃圾收集过程。

当我们了解到内存区域时,再看下引用类型与值类型:

值类型只需要一段单独的内存,用于存储实际的数据。
引用类型需要两段内存:
第一段存储实际的数据,它总是位于堆中
第二段是一个引用,指向数据在堆中的存放位置。

如果数据不是其他类型的成员,就会像图3-9所示那样存储。对于值类型,数据存放在栈里。对于引用类型,实际数据存放在堆里而引用存放在栈里。


简单的理解堆与栈,你可以通过小例子,慢慢伸延到不同场合。你会发现类、结构,如何声明类实例对象,怎么存储与什么关系等都是同一原理。
希望大家学习,多思考与探索其中奥秘多多分享受。
如果我说漏或有误地方,指各位多多指出,学习与交流会让我们不断进展。


posted @ 2012-04-07 18:38  IT小猪  阅读(650)  评论(0编辑  收藏  举报