GC工作原理
Garbage Collector(垃圾收集器)以应用程序的root为基础,遍历应用程序在Heap上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是已经死亡的哪些仍需要被使用。已经不再被应用程序的root或者别的对象所引用的对象就是已经死亡的对象,即所谓的垃圾,需要被回收。这就是GC工作的原理。
托管资源及非托管资源
1.值类型对象,在.NET中,所有的结构体及枚举都属于值类型,值类型存放在栈上,在使用时,超出其作用域就会被释放掉。
2.引用类型对象,托管堆中存放引用类型对象,因此GC的内存管理的目标主要都是引用类型对象。
3.非托管资源,.NET中常见的非托管资源有文件句柄、窗口句柄、网络连接、数据库连接等。非托管资源需要显式清除。.NET中提供释放非托管资源的方式主要是:Finalize() 和 Dispose()。最常见的代码方式是使用using()语法调用,自动执行Dispose接口:
using (var obj= new Obj())
{
//do sth
}
GC与内存管理
对象的生命周期
一个对象的生命周期简单概括就是:创建 -> 使用 -> 释放。
在.NET中一个对象的生命周期:
- new创建对象并分配内存
- 对象初始化
- 对象操作、使用
- GC垃圾回收
创建对象的主要流程:
GC垃圾回收
垃圾回收基本流程
1.标记:根据应用程序根指针Root遍历堆上的每一个引用对象,对于还在使用的对象(可达对象)进行标记。
2.清除:针对所有无引用对象进行清除。
3.压缩:把剩下的可达对象转移到一个连续的内存,Root根指针的地址修改为移动后的新地址。
代龄
GC的代龄分为0代、1代和二代
1.0代,最新分配在堆上的对象,从来没有被垃圾收集过。任何一个新对象,当它第一次被分配在托管堆上时,就是第0代(大于85000的大对象除外)。
2.1代,0代满了会触发0代的垃圾回收,0代垃圾回收后,剩下的对象升级为1代。
3.2代,当0代、1代满了,会触发0代、1代的垃圾回收,第0代升为第1代,第1代升为第2代。
大对象堆
分配在这个区域的对象都是大于85000字节的大对象。
这个区域不会被轻易回收,执行回收后,也不会压缩内存空间(因为对象大,成本高)。所以在实际工作中,应该尽量避免创建大于85000字节的大对象。
什么时候会进行垃圾回收?
- 创建新对象时,内存不足够创建新对象
- 内存不足溢出时
- Windwos报告内存不足时,CLR会强制执行垃圾回收
- CLR卸载AppDomian,GC回收所有
- 调用GC.Collect
- 其他情况
CLR两种类型的垃圾回收
- 工作站垃圾回收 :程序默认的回收机制。垃圾回收的过程,跑在触发垃圾回收的用户线程上,并使用相同的优先级。这种方式,优点是不会被挂起或延迟,缺点是需要与其它线程竞争CPU时间。当运行环境中只有一个CPU时,系统会自动采用工作站方式。
- 服务器垃圾回收 :针对的是高吞吐的服务器应用,回收过程跑在专用的高优先级线程上,而且默认是多线程在跑,所以效率更高,缺点是占用的资源会更多,而且由于线程之间的干扰和上下文切换,会影响整体性能。
通常普通应用,工作站回收就好。如果是服务器端的API服务,需要选择服务器回收。
设置垃圾回收方式,可以在 .csproj 文件中加入:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
设置true就是服务器模式,设置false就是工作站模式。不配置时,默认为工作站模式。