1 原则上禁止使用GC.Collect 方法
显式执行GC.Collect()有可能给内存回收增加负担,而且不一定能真正回收内存,垃圾回收周期是不确定的,由垃圾回收器引擎自动计算最佳的垃圾回收时间。在一个需要大量消耗内存的应用程序中,如果在某个确定的时间点上已经明确占用的内存不再需要,及时释放这些内存对提高应用程序的性能有显著的影响,这时就可以强制垃圾回收器执行回收周期。
GC.Collect方法强制垃圾回收器执行垃圾回收周期。在应用程序中调用GC.Collect方法可以强制垃圾回收,及时释放不再需要的大量内存。但是过于频繁的调用GC.Collect方法同样也会带来应用程序的性能问题。因为,开始执行垃圾回收线程时,垃圾回收器将挂起当前正在执行的所有线程,直到垃圾回收线程结束;另一方面,频繁的调用GC.Collect方法还将削弱垃圾回收器引擎的优化作用。而在通常情况下,垃圾回收器往往可以确定最佳的垃圾回收时间。原则上禁止使用,如果有特殊情况需要使用,要求进行代码评审
2 如果释放非托管资源,建议采用dispose模式
类实例经常封装对不受运行库管理的资源(如窗口句柄 (HWND)、数据库连接等)的控制。因此,应该既提供显式方法也提供隐式方法来释放这些资源。通过在对象上实现受保护的Finalize 方法(在 C#中为析构函数语法)可提供隐式控制。当不再有任何有效的对象引用后,垃圾回收器在某个时间调用此方法。
在有些情况下,可能想为使用该对象的程序员提供显式释放这些外部资源的能力,以便在垃圾回收器释放该对象之前释放这些资源。当外部资源稀少或者昂贵时,如果程序员在资源不再使用时显式释放它们,则可以获得更好的性能。若要提供显式控制,请实现由IDisposable接口提供的Dispose方法。在完成使用该对象的操作时,该对象的使用者应调用此方法。即使对对象的其他引用是活动的,也可以调用Dispose。
正例:
//基类模式.
public class Base: IDisposable
{
//实现 Idisposable接口.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
//其他状态 (托管 objects).
}
//释放状态 (非托管对象)
}
// 用 C# 析构函数来负责 finalization 的代码
~Base()
{
Dispose (false);
}
}
//子类模式.
public class Derived: Base
{
protected override void Dispose(bool disposing)
{
if (disposing)
{
//释放托管资源.
}
//释放非托管资源
//调用基类的Dispose方法
base.Dispose(disposing);
}
//子类不需要Finalize方法或者无参数的Dispose方法,因为它从基类继承了
}
3 如果有IO操作,建议使用Buffer缓冲区
说明:在IO操作时,输入输出流实际的长度未知的情况下,比如:网络流。可以先初始化一段缓存,再将流读出来的流信息写到内存流里面,这样可以提高性能并且安全
正例:
publicstaticbyte[] ReadFully(Stream stream)
{ //初始化一个8k的缓存
byte[] buffer = new byte[8192];
using ( MemoryStream ms = new MemoryStream() )
{
//返回结果后会自动回收调用该对象的Dispose方法释放内存
while ( true )
{
int read = stream.Read(buffer, 0, buffer.Length);
if ( read <= 0 )
{
return ms.ToArray();
}
ms.Write(buffer, 0, read);
}
}
}
4 不允许使用空析构函数
如果类包含析构函数,创建对象时会在 Finalize队列中添加对象的引用,以保证当对象无法可达时,仍然可以调用到 Finalize方法。垃圾回收器在运行期间,会启动一个低优先级的线程处理该队列。相比之下,没有析构函数的对象就没有这些消耗。如果析构函数为空,这个消耗就毫无意义,只会导致性能降低。因此,不要使用空的析构函数。
5 递归和循环 在循环体内避免耗资源的操作,如创建大对象,把这些操作提到循环体外面
说明:在循环体内如果有获取某个变量值的操作,且和循环变量无关的情况,可以把此操作提取到循环外,用临时变量替代此数值,避免无谓的耗资源
正例:
Database db = new Database();
DataTable dt = db.LoadSql(“select id from emps”);
foreach( int i = 0 ; i < 5000 ; i++ )
{
Emp objEmp = dt.select(“id=” + i, dt);
。。。
}
反例:
foreach( int i = 0 ; i < 5000 ; i++ )
{ Database db = new Database();
//这里读取数据库是昂贵的操作,且操作和循环无关, 应该用一个临时变量
//记录,并且在变量中使用
Emp objEmp = Business.FindEmp(“id=” + i, db.LoadSql(“select id from emps”));
}
正例:
Class objClassa=new Class();
foreach( Class objClassb in Classs)
{
objClassa.a=objClassb.b;
...
}
反例:
foreach( Class objClass in Classs)