CLR的核心功能:内存管理,程序集加载,安全性,异常处理,线程同步等等。可以被很多属于微软系列的开发语言使用。
堆栈内存分配:
堆 Heap: 进程堆,一个程序在运行是,进程对方引用类型变量的一块内存,全局唯一
栈 Stack: 线程栈,一个线程存放变量的一个内存,随着线程的生命周期存在的
引用类型:类/接口/委托 存放在堆上
值类型:结构/枚举 存放在栈上
{ //内存分配:线程栈 堆 ValuePoint valuePoint; valuePoint.x = 123; Console.WriteLine(valuePoint.x);//赋值后才能使用 ValuePoint point = new ValuePoint();//默认一定有无参数构造函数 } { //装箱拆箱 int i = 3; object oValue = i;//装修 值类型转引用类型 int k = (int)oValue;//拆箱 引用类型转值类型 } {//new一个对象的步骤 ReferenceTypeClass referenceTypeClassInstance = new ReferenceTypeClass(); referenceTypeClassInstance.Method(); //1 调用New 就会去先开辟内存 //2 把实例传递给构造函数 //3 执行构造函数 //4 引用返回 } //值类型的值,会随着对象的位置存储 //引用类型的值,是一定会存储在堆里面 //值类型的长度是确定,引用类型的长度不确定的,只有堆才能存放各种值 { string student = "大山"; string student2 = "APP";//共享 student2 = "大山"; Console.WriteLine(object.ReferenceEquals(student, student2)); //判断二者是不是同 // 输出 true student和student2指向的是同一个引用地址 享元模式 student2 = "大山1"; // new String("大山1") 会开辟一块新的内存 Console.WriteLine(student); //还是输出大山 字符串的不可变性 string student3 = string.Format("大{0}", "山"); Console.WriteLine(object.ReferenceEquals(student, student3)); //false 因为二者没有享元 先分配内存 然后计算 最终的结果才是 “大山” string student4 = "大" + "山"; Console.WriteLine(object.ReferenceEquals(student, student4)); //true 先计算了, 直接就得到大山 可以指向之前存在的内存块 string halfStudent = "山"; string student5 = "大" + halfStudent; Console.WriteLine(object.ReferenceEquals(student, student5)); //false 是先分配内存,然后再计算 }
托管堆垃圾回收 -- GC
1.值类型和引用类型在GC的区别
只有引用类型才需要垃圾回收 存放在托管堆上 值类型是存放在栈上的 一直存在内存中
2.托管资源和非托管资源的区别
托管在CLR上 如:new的对象,string
非托管资源:数据连接,文件流,句柄 非托管资源是需要手手动释放;
using 其实是c# 封装了非托管资源的连接
3.什么对象的内存,会被GC回收
对象访问不到了,就可以被GC回收
程序入口--找对象--建立一个标记--对象图---如果访问不到的就是垃圾
4.对象是如何分配再堆上
引用类型每次分配在堆上面的时候回检查当前空间是否足够 足够就分配,不足够就执行GC
5.什么时候会执行GC
1 程序在退出的时候会自动执行GC
2 string a="value" 设置 a=null; 会执行GC
3 创建对象的时候--会有一个临界点 当0代对象内存满了 GC会自动去清理0代对象
4 可以手动触发 GC.Conllect 然后直接执行GC 但是不建议频繁的去GC 因为回收也是需要消耗资源的
6.GC回收流程
首先将所有对象--全部对象标记为垃圾--开始一个一个检查,如果可以访问到,就标记一下为不是垃圾对象
然后会再次遍历去清除内存 清除之后产生不连续内存---然后进行地址移动--在压缩--最后修改变量指向
7.垃圾回收的策略
对象分代:3代
0代: 第一次分配到堆 就是0代
1代: 被回收一次之后就由0代变成1代,依然存在
2代: 被回收两次之后就由1代变成2代,依然存在
优先回收0代,提高效率,最容易也是多需要回收的
如果0代回收之后,内存仍然不够----就去找1代 -----2代
8.大对象:所存储的大小大于等于80000个字节的时候,同时大对象会被直接列举为2代
Dispose 主动清理 析构函数:被动清理
适用场景就是:数据库连接,文件流
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyCLRCore { /// <summary> /// 标准Dispose模式 /// /// 析构函数:被动清理 /// Dispose:主动清理 /// </summary> public class StandardDispose : IDisposable { //演示创建一个非托管资源 private string _UnmanageResource = "未被托管的资源"; //演示创建一个托管资源 private string _ManageResource = "托管的资源"; private bool _disposed = false; /// <summary> /// 实现IDisposable中的Dispose方法 /// </summary> public void Dispose() { this.Dispose(true); //必须为true GC.SuppressFinalize(this);//通知垃圾回收机制不再调用终结器(析构器) } /// <summary> /// 不是必要的,提供一个Close方法仅仅是为了更符合其他语言(如C++)的规范 /// </summary> public void Close() { this.Dispose(); } /// <summary> /// 必须,以备程序员忘记了显式调用Dispose方法 /// </summary> ~StandardDispose() { //必须为false this.Dispose(false); } /// <summary> /// 非密封类修饰用protected virtual /// 密封类修饰用private /// </summary> /// <param name="disposing"></param> protected virtual void Dispose(bool disposing) { if (this._disposed)//已经被释放的还可以不异常 { return; } if (disposing) { // 清理托管资源 if (this._ManageResource != null) { //Dispose this._ManageResource = null; } } // 清理非托管资源 if (this._UnmanageResource != null) { //Dispose conn.Dispose() this._UnmanageResource = null; } //让类型知道自己已经被释放 this._disposed = true; } public void PublicMethod() { if (this._disposed) { throw new ObjectDisposedException("StandardDispose", "StandardDispose is disposed"); } // } } }