CS3L5——构造函数、析构函数和垃圾回收
CS3L5——构造函数、析构函数和垃圾回收
本章代码关键字
1 | this //this代表类自己,可以显式的指定指明自己的成员 |
构造函数
在实例化对象时,会调用的用于初始化的函数,主要就是用来初始化成员变量,如果不写则默认存在一个无参构造函数
构造函数的声明和使用
没有返回值,函数名与类名相同,没有特殊需求时一般都是 public
的
类中允许自己申明无参构造函数的,但是结构体不允许
1 | class Person |
在 new
对象时,就会调用构造函数
1 | class Person |
输出:
1 | 无参构造函数被调用了 |
构造函数可以被重载,方便初始化函数,可以通过 this
来调用函数的对象自己
1 | class Person |
注意!如果一个类不实现无参构造函数而实现了有参构造函数,会失去默认的无参构造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 class Person
{
public string name;
public int age;
public Person()
{
name = "liuqi";
age = 18;
}
public Person(string name, int age):this()
{
this.name = name;
this.age = age;
}
}
internal class Program
{
static void Main(string[] args)
{
// Person p = new Person();
Person p = new Person("MrTang", 18)
}
}
构造函数的重用
可以通过 this
,重用构造函数代码 会先使用 this()
的构造函数
声明语法为:访问修饰符 构造函数名(参数列表) : this(参数1,参数2...)
如果重用 有参数的构造函数,则需要传入对应的参数,
可以使用被外部调用的构造函数的形参,也可以用表达式或者常量,只要参数类型正确即可
1 | class Person |
输出:
1 | 双参数构造函数被调用了 |
析构函数
当引用类型的堆内存被回收时,会调用该函数,对于需要手动管理内存的语言(如C++),需要在析构函数中做一些内存回收处理
但由于 C# 中由自动垃圾回收机制 GC,所以 C# 使用析构函数较少,一般用在某一个对象被垃圾回收的时候,做一些特殊处理
注意:析构函数在Unity的开发中几乎不使用
析构函数声明语法:~类名()
1 | class Person |
C#的垃圾回收机制
垃圾回收,英文简写GC (Garbage Collecter),垃圾回收的过程是遍历堆 (heap) 上动态分配的所有对象
通过识别他们是否被引用来确定那些对象是垃圾 哪些对象仍然要被引用
所谓垃圾就是没有被任何变量 对象引用的内容,垃圾就需要被回收释放
1 | internal class Program |
上面的代码中,p = null
后,之前 new
出来的 Person
对象就成为了垃圾,因为没有任何变量再引用它存在于堆上的数据了
垃圾回收有很多种方法,如:
- 引用计数(Reference Counting)
- 标记清除(Mark Sweep)
- 标记整理(Mark Compact)
- 复制集合(Copy Collection)
注意:GC 只负责堆(heap)内存的回收,引用类型都是存放在堆(heap)中的,所以它的分配和释放都通过垃圾回收机制来管理
栈(Stack)上的内存是由系统自动管理的,值类型在栈(Stack)中分配内存的,它们有自己的生命周期,不用对它们进行管理,会自动分配和释放
C#中内存回收机制的大概原理
C#将内存分为三代内存:0 代内存、1 代内存、2 代内存
其中,代 是垃圾回收机制使用的一种算法(分代算法)
新分配的对象都会被配置在第 0 代内存里,每次分配都可能会进行垃圾回收以释放内存
当 0 代内存满时而又要放入新数据时,就会就会触发垃圾回收
在一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾,会进行以下两步:
-
标记对象 从根(静态字段、方法参数)开始检查引用对象,
标记后为可达对象,未标记为不可达对象,不可达对象就被认为是垃圾
例如一个实例化后没有变量再引用的对象,就是垃圾
-
搬迁对象,并压缩堆(这时会挂起执行托管代码进程),释放未被标记的对象,搬迁可达对象,修改引用地址
例如 0 代内存里可达对象就会被搬迁到 1 代内存,不可达的对象被当作垃圾处理回收,搬迁完成后需要将对应地址进行改变为连续的数值
若 1 代内存满后也会触发垃圾回收,同时也会触发 0 代垃圾回收(不管 0 代内存满没满)
而 1 代可达对象也搬迁到 2 代内存,其他步骤类似于上个步骤若 2 代内存满后也会触发垃圾回收,同时也会触发 0 代和 1 代的垃圾回收,步骤同上
0 代和 1 代内存的速度会快于 2 代内存速度,因为在 2 代内存中可能存在的垃圾更多
85000字节(83kb)以上的对象就是大对象,大对象总被认为是 2 代内存
目的是减少性能消耗,提高性能,不会对大对象进行搬迁压缩
主动触发GC的方法
GC是可以手动触发的,而不需要等待内存满后触发
1 | internal class Program |
建议在类似于读取等需要玩家等待的时段进行主动GC,例如显示读取条时