GC即Garbage Collection,垃圾回收的意思。C#的垃圾回收器Garbage Collector会跟踪并回收托管内存中分配的对象。垃圾回收器会周期性地执行垃圾回收,回收托管内存中没有被有效引用的对象。
我们知道C#中的变量分为值类型和引用类型。值类型的变量存储在栈(stack)上,在作用域结束时自动释放,并不需要GC回收。引用类型的变量存储在堆(heap)上,在.NET 这种托管环境下,堆由CLR (公共语言运行库Common Language Runtime)进行管理,所以又称为“托管堆(managed heap)”。大部分对象都是托管资源,GC能够自动回收托管堆上的托管资源。但是GC不能自动回收非托管资源,常见的一般有操作系统资源对象,如:网络连接,文件流,数据库连接等。所以我们需要编写回收非托管资源的代码。
释放非托管资源的正确方式,是为类型实现IDisposable接口的Dispose方法,当你需要释放类型的资源的时候,应该显式的调用Dipose方法。这里还有一个C#的语法糖,就是使用using程序块,在离开using程序块的时候,CLR会自动调用类型所创建对象的Dipose方法。如在Unity3D中使用WWW并释放网络连接资源的例子:
public class ExampleClass : MonoBehaviour
{
public string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
IEnumerator Start()
{
using (WWW www = new WWW(url))
{
yield return www;
Renderer renderer = GetComponent<Renderer>();
renderer.material.mainTexture = www.texture;
}
}
}
Dispose的实现示例如下:
public class Foo : IDisposable
{
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//释放托管资源
}
// 释放非托管资源
disposed = true;
}
}
//既释放托管资源,又释放非托管资源
public void Dispose()
{
Dispose(true);
//将对象从垃圾回收器链表中移除,
//从而在垃圾回收器工作时,只回收托管资源,而不执行对象的析构函数
GC.SuppressFinalize(this);
}
//以防开发人员忘记调用Dispose
~Foo()
{
Dispose(false);
}
}
一些额外的Tips
-
String与StringBuilder
String类型的变量是不可变的,当一个String类型的变量被初始化以后,任何对它的修改都会创建一个新的对象,当对一个字符串有频繁修改的需求时,会影响性能,而StringBuilder则没有这个问题。因此遇到上述情况用StringBuilder更合适。
-
Array,ArrayList和List
数组Array
优点:
- 数组在内存中是连续存储的,索引速度非常快
- 修改方便
缺点:
- 声明时必须指定长度。长度过长内存浪费,长度不够造成溢出错误
- 插入数据麻烦
针对于数组的这些缺点,C#中最先提供了ArrayList对象来克服这些缺点
ArrayList
ArrayList继承了IList接口
优点:
- 无需指定长度,它的大小是按照其中存储的数据来动态扩充与收缩的
- 可以方便地进行数据添加与移除
缺点:
- 类型不安全。ArrayList允许插入不同类型的数据,因为ArrayList会把所有插入其中的数据都当作为object类型来处理。
- 性能损耗问题。既使我们保证在插入数据的时候都很小心,都插入了同一类型的数据,但在使用的时候,我们也需要将它们转化为对应的原类型来处理。这就存在了装箱与拆箱的操作。
装箱:就是将值类型的数据打包到引用类型的实例中
拆箱:就是从引用数据中提取值类型
装箱与拆箱的过程是很损耗性能的。
泛型List
正是因为ArrayList存在不安全类型与装箱拆箱的缺点,所以在C#2.0后出现了泛型的概念。而List类是ArrayList类的泛型等效类。它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。
So, just use List.
网友评论