栈
类似于电梯
特征
数据只能从栈的顶端插入和删除
把数据放入栈顶称为入栈(push)
从栈顶删除数据称为出栈(pop)
后进先出
内存中的栈,是由系统管理的(.Net框架)
堆
堆是一块内存区域,与栈不同,堆里的内存能够以任意顺序存入和移除
GC(Garbage Collector)垃圾回收器
CLR的GC就是内存管理机制,我们写程序不需要关心内存的使用,因为这些都是CLR帮
我们做了。
比如 当我们变量不再使用时候,就会把这块内存区域取消标记,定时检测内存区域有没有人使用,没人人使用就会回收内存区域 或者 被其他需要内存的变量占用
当我们创建变量的时候,就会分配一个没有被占用的内存空间,并在这个内存空间上标记,不使用后才取消标记
比如我们大学宿舍,每个学生都分配有床位,每个床位都标记有学生姓名,但如果有人退学之后,那这个床位就空了,我们就要把这个床位取消学生姓名标记,之后,如果有新生入学,就可以分配没有占用的床位给他,宿管也会定期检查床位是由哪些学生在用,如果是空着的,但是被占用了,那就取消标记,类似于在记录本里取消标记
值类型和引用类型
类型被分为两种:值类型(整数,bool struct char小数,枚举)和引用类型(string数组自定义的类,内置的类)。(引用类似于c++的指针,都是地址,但c#没有指针概念,)
值类型只需要一段单独的内存,用于存储实际的数据,(单独定义的时候放在栈中)
引用类型需要两段内存:
第一段存储实际的数据,它总是位于堆中
第二段是一个引用,指向数据在堆中的存放位置
字符串是存储在静态存储区的,有利于重复引用,节约内存
static void Main(string[] args)
{
int a = 123;
float b = 34.5f;
bool c = false;
string name = "djfj";
int[] array1 = new int[] { 23, 12, 31, 21 };
string[] arrays = new string[] { "www", "wwws","wwss" };
}
比如对于字符串数组,arrays变量就是在栈的地址,引用了在堆存储的字符串数组的地址,堆的字符串数组 地址又引用了静态存储区的字符串,所以本质上字符串还是存储在静态存储区
对于单个字符串,则是直接引用到静态存储区的地址,比如name就是指向静态存储区的地址
字符串是不可以修改的
string s1 = "张三";
s1 = "李四";
只是改变了引用,也就是改变了s1的地址,实际上没有改变静态存储区的字符串
关于内存回收
堆中的对象是由垃圾回收器来管理的。垃圾回收器使用一种叫做“引用计数”的算法来帮助确定何时可以释放对象所占用的内存空间。在这个算法中,每个对象都有一个引用计数器,用来记录指向该对象的引用数量。
当一个对象被引用时,它的引用计数加一;当一个引用失效或者被销毁时,对象的引用计数减一。当引用计数减为零时,垃圾回收器会判断该对象已经没有被引用,可以安全地释放其占用的内存空间。
然而,在实际的 .NET Framework 和 .NET Core 中,并不使用引用计数作为主要的垃圾回收算法,而是采用基于“可达性分析”的算法。可达性分析会从一组被称为“根”的起始对象开始,逐步遍历对象图,标记所有可以通过引用链访问到的对象,最终将未标记的对象视为垃圾进行回收。