CLR VIR C#

1、.NET运行机制:CLR:公共语言运行时;

  运行步骤:C#文件——》C#编译器——》托管模块(中间语言IL和元数据)——》多个托管模块或元数据合并成程序集——》CLR加载——》JIT(即时编译)Just-In-Time讲IL编译成本机CPU指令——》本机机器码——》CPU;

派生:指的是继承;

2、值类型和引用类型

  值类型赋值=复制完整数据,你我各一份,互不干扰,拆箱出来永远是副本;

  引用类型赋值=复制地址,大家共同在堆上,同一对象,一处修改全部看见;

3、GC机制

  针对的是托管堆内存,GC负责自动管理托管堆中的对象生命周期;   托管堆:是CLR专门用来分配和管理对象内存的一块区域;GC.ROOT:GC查找那些对象还活着的起点;

  (1)标记:1.先暂停全部用户线程;2.标记全部待删除状态,从GC.ROOT根出发标记是否存活;3.遇到已标记的就跳过,防止死循环;4.可达:根能顺着引用访问到存活,不可达:等待回收。

  (2)清除:回收垃圾内存,存活对象不动,会产生碎片;

  (3)压缩:移动存活对象,消除碎片,但是必须更新所有引用地址,会增加STW(暂停全部用户线程)时间,压缩不是每次都执行,CLR会判断碎片比较严重才压缩。

  分代:0代,1代,2代;分代可提高GC效率,优先处理最容易死亡的短命对象,减少每次都扫描所有对象的成本。

  (1)0代默认new对象,用完马上释放,若对象字节>85000,这种大对象直接进2代,不走0代和1代;

  (2)0代GC存活对象继续使用可能会晋升到1代,依次,如果还是存在,2代GC存活。

  (3)GC最多就2代

  强制垃圾回收:

  (1)GC.MaxGeneration=2;

  (2)Gc.Collect(n):回收n代及下面所有代;

  (3)Forced强制回收,Opeined看效益选择性回收;

  (4)Blocking=true阻塞等待GC结束,等于False后台并发不等待回收完成;

  (5)优先交给CLR自动GC,业务尽量避免手动Collect。

  两大主模式:工作站GC、服务器GC。

  子模式:并发(后台)GC、非并发GC

    并发(后台)GC:生效范围仅2代完整GC,有独立后台GC线程,0/1代依旧STW,内存占用大,先处理完然后在停顿,只有最后一小段STW,非并发全程STW;

  GC负责对象托管内存,但是对于:文件、Socket、数据库连接等资源,我们通常要及时释放;

  IDisPlsable:这个对象拥有需要主动释放的资源;

  Using不是GC,它会自动调用Dispose;

  Finalier是CLR提供的最后一道资源清理机制,如析构函数。

 

  GC——》Finalizer——》IDisposable——》Using;

  托管内存——》GC负责——》自动回收;

  Using——》自动调Dispose;

  

1. 线程栈:存局部变量、引用地址;自动释放,不归 GC 管。
2. 托管堆:存 new 出来的对象实例;由 GC 管理回收。
3. 值类型存数据本身;引用类型栈存地址,堆存真实对象。
4. 值类型不一定就在栈,要看它被嵌套在哪里。

  

串起来就是:C#代码——》IL——》CLR——》对象/类型——》值类型/引用类型——》装箱/拆箱——》托管堆——》GC——》Dispose/Using/Finalizer.

4、异常

  通常具体异常在前,通用异常在后;

  Finally:主要用于确保清理代码执行,不管有没有异常都会执行;

  Threw和Threw ex的区别:

    threw:重新抛出异常,并保留原始异常堆栈的信息;

    threw ex:会重新抛出异常,可能导致原始堆栈发生变化。

  异常会逐级往上抛,比如异常嵌套的话,会一直找上层有没有处理异常,如果有处理则会在Catch中执行;

 5、进程和线程

  进程:它是一个正在运行的程序实例,可比作成:我们打开某个应用、微信等;

  线程:它是进程内部执行实际代码,由操作系统调度的基本执行单元,进程提供资源和环境,线程负责真正执行代码,1个进程可以有很多个线程;

  为什么要开多线程:线程耗时操作长时间阻塞主线程,提高响应能力;

  并发:多个任务在时间上交替执行,不一定是同时执行;

  并行:多个任务在同一时间执行;

  线程不能无限创建,会产生:(1)内存占用,线程栈;(2)消耗操作系统资源;(3)CPU上下文切换;(4)调度成本。

  线程什么时候获得CPU执行时间,由操作系统决定,所以线程执行顺序是不确定的。

  ThredPool线程池:一个任务对应一个线程,任务执行完成后,等待下一个任务,即线程可复用。简单说就是线程池里面可以有很多线程,但是这些线程不是用完就马上回收,它可下次在复用。

  Thread.Start():启动线程,让操作系统调度,该线程执行的指定方法,不是Start马上CPU就调用,而是进入可调用的状态;启动线程,让其参与CPU调度;

  Thread.Join:它是等待Thred这个目标线程执行结束,谁调用就等待谁,指的当前调用的线程;当前线程Thread等待结束;

  只要前台线程正在运行,进程通常不会退出,如果前台线程都结束了,那么进程退出时,后台进程可能会被终止;所以前台线程决定进程是否存活,后台线程不决定。

  Thread Pool——》复用线程执行任务——》减少频繁创建线程的成本。

6、Task、async&await

  Task:它代表一个任务的执行过程,Task不等于Thread,它代表这件事情,Thread代表实际执行代码的线程,Task是我要完成这件事情,Thread是谁来执行这件事情;

  Task关注的是任务,Thread关注的是线程;Task更抽象是一种高层的任务。

  Task.Run:创建一个任务,它通常交给线程池线程来执行,任务执行完成,表示Task进入完成状态;

  Task.Wait:当前线程等待Task执行执行完成,它是同步阻塞等待;

  Task<T>:返回结果.Result——》Task没完成——》等待——》Task完成——》获取结果;它和Task.Wait类似都可阻塞当前线程;

  Task的状态:WaitingToRun——》Running——》RunToCompletion;还有失败:Faulted;取消:Canceled;

  Task.WaitAll:多个Task并行执行,它会阻塞当前线程,直到Task执行完成;

  Task通常使用线程池,它也可能不需要额外的线程,Task.Run通常会把任务安排到线程池上执行,并返回Task;

  Task表示是一个要完成的异步操作,而Thread表示实际执行代码的线程,一个Task可以在线程池线程上执行,也可以通过其他方法完成,所以Task和Thread它不是对应的关系;

  async:它不是开启异步的意思,它是告诉编译器这个方法会使用await,并且这个方法可能会执行异步方法,可理解它是一个异步方法;

  await:它等待的是Task,不是线程,Task没完成,当前async会暂停,不阻塞的当前的线程;

  wait是线程等待,await是方法等待;

  async它本身不会创建新的线程,它不会阻塞当前线程;

  Task.WaitAll和Task.WhenAll:他们都是等待多个任务完成,最大区别在于WaitAll会阻塞当前线程,WhenAll不会阻塞当前线程,它会配合await是异步等待;

  Task.WhenAny:它是多个任务谁先完成我就继续,不会自动取消其他任务,其他任务会正常执行完成;

  CPU计算可考虑使用Task.Run(),涉及到:网络,数据库,文件等待,优先使用异步async,await,不要简单的task.Run+阻塞。

  .Result可能会导致线程阻塞,建议要么就是一直异步到底。

 

  

posted @ 2026-08-31 08:33  淡 淡  阅读(4)  评论(0)    收藏  举报