一、何为GC
数据是存储在内存中的,而内存又分为Stack栈内存和Heap堆内存
Stack栈内存 | Heap堆内存 |
---|---|
速度快、效率高 | 结构复杂 |
类型、大小有限制 | 对象 |
只能保存简单的数据 | 引用数据类型 |
基础数据类型、值类型 | - |
举个例子
var c= new Customer{
id: 123,
name: "Jack"
address: "珠海"
}
在堆内存中就保存了信息
#1000 | ||
---|---|---|
123 | Jack | 珠海 |
而在栈内存中仅保存了需要调用的地址c* = 1000
——reference
当删除时,需要先删除堆内存的数据,再删除栈内存的数据,然而如果先删除了栈内存的数据,那么对内存中的数据就再也无法找到,也无法删除,无法重复利用,就会造成内存泄漏。
因此,为了便捷,如JAVA、C#等语言引入了垃圾回收机制,使得程序员只需要关注于对象本身即可。
如果一段对象的引用数量为0,则代表对象的声明周期结束。
二、GC是如何工作的
运行垃圾回收的成本很高,需要不断地遍历所有数据,因此使用了复杂的机制来解决高效运行问题。——Generations分代回收
将数据对象分成三组
G0 | G1 | G2 |
---|---|---|
暂时性的对象 | 中长期对象 | 长期对象 |
每次运行GC都检查 | 检查频率下降 | GC偶尔来检查 |
内存不足时,GC会强行清理所有对象
GC不止处理垃圾清理
- [ 标记、清理堆内存中的死掉的对象]
- [ 压缩内存、消除间隙,提高对象的创建、读取效率 ]
不过GC不会处理过大的内存区块
GC独立线程
- [ GC跑在独立的后台线程中 ]
- [ 每次运行都需要付出代价,需要消耗计算资源 ]
- [ 尽可能的减少运行频率、并且尽可能提高运行效率 ]
三、析构方法and终结器
终结器(以前称为析构函数)用于在垃圾回收器收集类实例时执行任何必要的最终清理操作。在大多数情况下,通过使用System.Runtime.InteropServices.SafeHandle或派生类包装任何非托管句柄,可以免去编写终结器的过程。
若无必要,不要使用
使用终结器会造成性能的损失。
代码举例
public class AnywayClass
{
public AnywayClass()
{
Console.WriteLine("AnywayClass类创建");
}
~AnywayClass()
{
Console.WriteLine("AnywayClass类销毁");
}
}
class Program
{
static void Main(string[] args)
{
var anyway = new AnywayClass();
Console.WriteLine("程序结束");
}
}
但是运行后会发现,程序并不会输出“AnywayClass类销毁”,要判断当前实例是否还会被引用,是根据语句的区域决定的,也就是说,它的作用域是整个main方法,因此垃圾回收是在整个main方法外面,因此看不到析构方法的输出。
因此要看到输出,就要降低对象的作用域。
public class AnywayClass
{
public AnywayClass()
{
Console.WriteLine("AnywayClass类创建");
}
~AnywayClass()
{
Console.WriteLine("AnywayClass类销毁");
}
}
public class SecondClass : AnywayClass
{
public