对于.Net所写一般程序来说,都属于托管程序,内存的释放和回收是由Garbage Collector完成。但是相对于栈上内存操作而言,GC回收堆上的内存,会消耗更多的CPU时间,这方面的内容可以参看这篇文章。
http://blog.csdn.net/knight94/archive/2006/08/05/1023352.aspx
因此如果让GC不停的释放和回收内存,会造成程序性能的下降。
例如对于如下这段程序而言。
protected override void OnPaint(PaintEventArgs e)
{
using( Pen penBlack = new Pen( Color.Black, 2f ) )
{
e.Graphics.DrawLine( penBlack, 10, 10, 100,10 );//Draw a black line
}
base.OnPaint (e);
}
虽说每次调用完OnPaint后,penBlack都调用了Dispose方法,但是Dispose不同于原来C或者C++的Delete方法,还是需要GC去回收内存。那么当此函数频繁被调用,那么在内存中产生的垃圾就会越来越多,GC需要花很多时间去回收它们,这样会造成程序性能下降。
那么,如何避免呢,或者说如何减少垃圾的产生呢。其实对象都是通过new来产生的,只要减少用new产生对象的次数,就可以减少垃圾的产生。只要把握到问题的关键点,那么相应的方法就有如下几个。
第一个就是,使用成员而代替局部变量,对于上面的例子,可以改成如下形成。
private Pen penBlack = new Pen( Color.Black, 2f );
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawLine( penBlack, 10, 10, 100,10 );//Draw a black line
base.OnPaint (e);
}
可能很多人从C或者C++转型到使用C#,按照以前C或者C++的概念,尽量少使用全局变量,多使用局部变量;由于C#无法像C或者C++去显式释放内存,那么与其对象频繁需要GC回收,不如减少这样操作,从而造成效果会更好。
第二个就是公用的对象,可以采用静态成员的方法。例如,对于上面例子中的penBlack可能其他类型也需要使用,防止在其他类型也去创建,可以采用静态全局成员,即按照如下来实现。
public class MyPens
{
private static Pen penBlack;
public static Pen Black
{
get
{
if( penBlack == null )
penBlack = new Pen( Color.Black, 2f );
return penBlack;
}
}
private MyPens(){}
}
对于.Net系统提供的“Pens”和“Brushes”这两个类,提供了一般常用的Pen和Brush,.Net提供这两个静态类估计也是出于此考虑的。因此在进行绘画的时候,可以先考虑这两个类型所提供的静态对象。
最后一个方法,主要是针对不可改变的原子类型,例如对于string类型来说,就是这种类型。
例如:
string strValue = "Hello";
strValue += " World";
对于如上的语句来说,按照一般类型来说,都会在原有对象进行处理,但是对于不可改变的原子类型而言,对于原有对象的修改,会产生新的对象。因此这两条语句会产生两个对象,第一个字符串对象是“Hello”,另一个就是“Hello World”。
假如这种修改操作很频繁,那么意味着产生对象也很多,也就是说等待GC回收的垃圾就很多。为了避免此类现象发生,要么选用非原子的替代类型,要么在处理手法进行修改。
对于string类型来说,替代类型为StringBuilder,这也就是为什么用StringBuilder替换string进行大型字符串操作效率高的原因。
至于处理手法的改进,就string而言,可以采用string.Format,例如:
string strValue = string.Format( "Total value is {0}!", nValue );
方法都说完了,怎么使用在于自身灵活的掌握。不过这里要注意的一点就是,这里所说的都是针对引用类型对象,而对于在值类型来说,并不需要这样处理,因为它们分配在栈上。