一.CLR 和Core CLR

1.什么是CLR

Common Language Runtime 通用语言运行时 .
公共语言运行时(CLR)是一套完整的、高级的虚拟机,它被设计为用来支持不同的编程语言,并支持它们之间的互操作。 公共语言运行时(Clr)是一套完整的、高级的虚拟机,它被设计为用来支持不同的编程语言,并支持它们之间的互操作,是应用和系统通信的桥梁.
如:写某种高级语言—要在硬件(操作系统OS—-x86 x64 windowslinux mac unix)运行
—-1分别编译—-2一套编译需要一个适配器(插座) 运行时环境—-写的高级语言-要在硬件运行-操作系统OS-x86x64 windows slinux mac unix-1分别编译-2一套编译需要一个适配器(插座)

2.CLR位置

image.png

3.NET Core .net Core

COMMONINFRASTURE COMMONINFASTURE
image.png

4.NET 5

image.png

5.CLR功能标准

1.基础功能,对其他的特性有广泛影响的功能。
A.垃圾回收
B.内存安全和类型安全
C.对编程语言的高级支持
2.次要功能,由基础功能发展而来的、但不是必须的功能:
A. AppDomains程序隔离
B.程序安全与沙盒
3.其他功能,运行时环境需要的、但并不依赖基础功能的特性。这些功能帮助我们建立了一个完整的编程环境。
A.版本管理
B.调试、性能分析
C.互操作

6.CLR和Core CLR

CLR是.NET Framework,4万多行代码
Core CLR就是全新一套.NET Core,剥离了一些依赖,只有2万多行代码
底层实现是C# / C++——-GC和JIT
功能都差不多的

二. CLR-GC

1.什么是GC

GC(Garbage Collection)垃圾收集,指的是在.NET中垃圾内存收集的机制。
因为程序运行需要内存而内存不是无限的,GC的诞生减轻了程序员的变成负担,是资源更加合理的分配。

GC(垃圾收集)主要由Allocator(分配器)和Collector(收集器)组成
image.png

2.什么是推栈

栈和堆(托管堆)都存在于进程的虚拟内存中,为程序运行提供存储空间。

栈(Stack)是操作系统在建立线程时,为这个线程建立的存储区域。

堆(Heap)是应用程序在运行的时候请求操作系统分配给自己内存,是申请-给予的过程。

由于从操作系统管理的内存分配所以在分配和销毁时都要占用时间,所以用堆的效率低的多

简单理解:线程栈N—-进程堆1

3.栈(Stack)

1.栈中存储值类型(参数变量);
2.栈上是先进后出原则;
3.栈是自维护的,当元素不再被使用会被抛出;
4.栈空间较小,访问速度快;
线程栈,程序中会有多个线程栈,
image.png

4.堆(Heap)

1.堆(也叫做托管堆)存储引用类型;
2.堆受垃圾处理器GC管理;
3.堆没有访问限制,按地址索引;
4.堆空间较大,访问速度没有栈快;
5.对象是连续摆放的(一般来说,像数组 )
进程堆,进程唯一,需要GC (垃圾回收)
image.png

5.C#堆栈如何分配?

值类型分配在栈上面?
栈是一开始就分配的空间的
引用类型分配在堆上面?
引用类型要放在堆—因为不知道长度(大小),不确定长度的得放到堆上去

值类型里面的引用类型?-堆里面
引用类型里面的值类型?-堆里面

注意:引用类型里面的引用保存的也是地址,指向另外一块儿内存
image.png

6.String 字符的分配

如: string s=”张三” 然后 s = “李四”,该内存是如何操作的呢?
String引用类型,为了避免移动,会在内存重新申请一个李四,没有修改而是新增,不可变性
所以对象可以重用,别的线程如果也用张三字符串,就可以直接重用,这叫字符串的享元模式

7.值类型和引用类型在编译后的操作

new TestClass() [实例化一个类]
new object+ctor
1先申请内存
2指定类型
3Xxx
4构造函数

new TestStruct() [实例化一个 结构]
call ctor
直接分配对象的内存已经分配

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1697695/1610431835671-a5100e77-2b74-4531-882f-cbcb43b80656.png#align=left&display=inline&height=254&margin=%5Bobject%20Object%5D&name=image.png&originHeight=312&originWidth=575&size=197663&status=done&style=none&width=469)

三.内存分配(Allocator) ?

1.分配器调用时需要以下信息

-需要分配的大小
-线程专用的分配上下文(Allocation Context)-
-标记,如对象是否有析构函数

2.分配过程

按照对象的大小分类:
-小对象(小于85,000字节)
-大对象(大于或等于85,000字节)

要点:进程堆-多线程-分配上下文(线程专用)—空隙用freeobject填充
析构函数~析构队列 析构函数~析构队列

四.内存收集Collector

1.为什么需要垃圾收集

内存是有上限的,不可能无止境的分配空间,因此就产生了GC(Garbage Collector)的需求.

2.垃圾回收的好处

1.提高了软件开发的抽象度
2.程序员可以将精力集中在实际的问题上而不用分心来管理内存的问题;3.可以使模块的接口更加的清晰,减小模块间的偶合;
4.大大减少了内存人为管理不当所带来的Bug;
5.使内存管理更加高效(但肯定没有优秀的原生高效)。

总的说来就是GC可以使程序员可以从复杂的内存问题中摆脱出来,从而提高了软件开发的速度、质量和安全性。


3.什么样的对象需要垃圾回收

  1. 托管资源

托管资源一般是指被CLR控制的内存资源,这些资源的管理可以由CLR来控制,例如程序中分配的对象,作用域内的变量等,大部分对象都是托管资源。

非托管资源是CLR不能控制或者管理的部分,这些资源有很多,比如文件流,数据库的连接,系统的窗口句柄,打印机资源,需要调用Dispose方法只要涉及到第三方应用的—-数据库连接—-using—-用完后dispose释放—其实是因为封装的API里面写好了—-没用using也释放了( 1被dispose你不知道 2没有dispose但是最终还是释放的,就会慢一些)

  1. 存在堆里(含值类型+引用类型)

image.png

4.回收流程

1.标记阶段(Mark phase)
Mark-Sweep标记清除阶段,先假设heap中所有对象都可以回收,然后找出不能回收的对象,给这些对象打上标记,最后heap中没有打标记的对象都是可以被回收的,在第一个阶段开始后整个进程都是被挂起的

从roots出发寻找,两类: 从根出发寻找,两类:

  1. 已经初始化了的静态变量
  2. 线程仍在使用的对象(Stack + CPU Register>)

2.计划阶段(Plan phase)
3.重定位阶段(Relocate phase)
4清扫阶段(Sweep phase)
5.压缩阶段(Compact phase)
Compact压缩阶段,对象回收之后heap内存空间变得不连续,在heap中移动这些对象,使他们重新从heap基地址开始连续排列,类似于磁盘空间的碎片整理。
image.png
注意:整个回收前后:
内存移动 > 地址变更 > 取消阻塞 > 继续运行

5.三个假设

  1. 对象越新,生存期越短;
  2. 对象越老,生存期越长;
  3. 回收部分资源,快过全部回收。

    能逃得过第一次清洗的就更容易逃过第二次

6.分代策略

image.png

7.什么时候GC

  1. new对象时—临界点allocator—自动GC
  2. Windows报告内存不够
  3. GC.Collect()强制GC
  4. 程序退出或者卸载AppDomain

8.各代空间大小

实际上是没有具体值的,由CLR管理。
初始化时就会给出0代/1代的预算,然后动态调节!
GC后,对象几乎没有留存,那么就会减小预算,加快回收频率,每次速度快

如果GC后有很多对象留存,那么就会增加预算,降低回收频率,每次回收内容多

如果空间不够,就会全面回收,再不够就OutOfMemory了

9.怎么用GC.Collect

大多数情况下,应该让GC自己决定回收时间,以更好的控制各代预算,也避免GC降低响应 ,如果知道大量对象无效,可以手动GC,尤其是2代/大对象;

  1. GC.Collect(2);

image.png

10.GC两种形式

WorkStation GC—-低能耗— 工作站GC-低能耗

  1. 单线程回收—-利用进程的线程
  2. 空间小—频繁回收—每次回收少

Server GC—-高性能—-ASP.NET Core默认
a.多线程多区域并行回收—CPU的核分配线程
b. 空间大—回收频率低—回收数据多

image.png

11.GC三种模式

都是针对2代—-o/1太快
Non-Concurvent GC—-不并行GC—-常规GC,阻塞式
Concurvent GC ——并行GC—-GC时还可以分配—-解决阻塞
Background GC—-新的—-跟Concurvent一样—-但是增加了o/1额外申请空间
image.png
总之:3个模式 x 2个形式

五.NET Core-Docker 新变化

Docker—-资源限制—-单个程序越小越好—-微服务—-Funcation as a Service
Web架构目标是高性能—-冲突了—-低能耗

  1. 默认GC堆大小:容器上cgroup内存限制的最大值20MB或最大值的75%,支持小内存,都能活得下去—-server gc 4G—-Java会不断申请内存超出限制就挂掉了—但是.NET Core不会的,知道自己的上限,2代不够还能 outofmemory—-容器感知和自适应 外置存储器—容器感知和自适应

  2. 每个GC堆的最小保留段大小16MB,支持大量内核和小内存限制(创建的堆segment少)—-避免多内核低内存创建太多的segment

    六.Docker CPU支持

    ASP.NET Core默认Server GC
    Docker限制是单CPU,就是Workstation GC
    还是容器自适应