.NET 性能优化方法总结

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

.NET性能优化方法总结Ver1.02009-1-20目录1.C#语言方面...41.1垃圾回收...41.1.1避免不必要的对象创建...41.1.2不要使用空析构函数★...41.1.3实现IDisposable接口...41.2String操作...51.2.1使用StringBuilder做字符串连接...51.2.2避免不必要的调用ToUpper或ToLower方法...51.2.3最快的空串比较方法...61.3多线程...61.3.1线程同步...61.3.2使用ThreadStatic替代NameDataSlot★...71.3.3多线程编程技巧...71.4类型系统...81.4.1避免无意义的变量初始化动作...81.4.2ValueType和ReferenceType.81.4.3尽可能使用最合适的类型...91.5异常处理...101.5.1不要吃掉异常★...101.5.2不要吃掉异常信息★...101.5.3避免不必要的抛出异常...101.5.4避免不必要的重新抛出异常...101.5.5捕获指定的异常,不要使用通用的System.Exception.101.5.6要在finally里释放占用的资源...111.6反射...111.6.1反射分类...121.6.2动态创建对象...121.6.3动态方法调用...121.6.4推荐的使用原则...121.7基本代码技巧...131.7.1循环写法...131.7.2拼装字符串...131.7.3避免两次检索集合元素...131.7.4避免两次类型转换...141.7.5为字符串容器声明常量,不要直接把字符封装在双引号里面。...141.7.6用StringBuilder代替使用字符串连接符“+”.141.7.7避免在循环体里声明变量,...151.8Hashtable.151.8.1Hashtable机理...151.8.2使用HashTale代替其他字典集合类型的情形:...161.9避免使用ArrayList。...161.10从XML对象读取数据...171.11避免使用递归调用和嵌套循环,...171.12使用适当的Caching策略来提高性能...172.Ado.Net172.1应用Ado.net的一些思考原则...182.2Connection.182.2.1在方法中打开和关闭连接...182.2.2显式关闭连接...182.2.3确保连接池启用...192.2.4不要缓存连接...192.3Command.192.3.1使用ExecuteScalar和ExecuteNonQuery.192.3.2使用Prepare.192.3.3使用绑定变量★...192.4DataReader202.4.1显式关闭DataReader202.4.2用索引号访问代替名称索引号访问属性...202.4.3使用类型化方法访问属性...202.4.4使用多数据集...202.5DataSet212.5.1利用索引加快查找行的效率...212.使用DataView..213.ASP.NET.213.1减少往返行程(ReduceRoundTrips)...213.2避免阻塞和长时间的作业...223.3使用缓存...223.4多线程...223.5系统资源...233.6页面处理...233.7ViewState.234.JScript244.1JScript性能优化的基本原则...244.2JScript语言本身的优化...244.3DOM相关...274.4其他...281.C#语言方面1.1垃圾回收垃圾回收解放了手工管理对象的工作,提高了程序的健壮性,但副作用就是程序代码可能对于对象创建变得随意。1.1.1避免不必要的对象创建由于垃圾回收的代价较高,所以C#程序开发要遵循的一个基本原则就是避免不必要的对象创建。以下列举一些常见的情形。1.1.1.1避免循环创建对象★如果对象并不会随每次循环而改变状态,那么在循环中反复创建对象将带来性能损耗。高效的做法是将对象提到循环外面创建。1.1.1.2在需要逻辑分支中创建对象如果对象只在某些逻辑分支中才被用到,那么应只在该逻辑分支中创建对象。1.1.1.3使用常量避免创建对象程序中不应出现如newDecimal(0)之类的代码,这会导致小对象频繁创建及回收,正确的做法是使用Decimal.Zero常量。我们有设计自己的类时,也可以学习这个设计手法,应用到类似的场景中。1.1.1.4使用StringBuilder做字符串连接1.1.2不要使用空析构函数★如果类包含析构函数,由创建对象时会在Finalize队列中添加对象的引用,以保证当对象无法可达时,仍然可以调用到Finalize方法。垃圾回收器在运行期间,会启动一个低优先级的线程处理该队列。相比之下,没有析构函数的对象就没有这些消耗。如果析构函数为空,这个消耗就毫无意义,只会导致性能降低!因此,不要使用空的析构函数。在实际情况中,许多曾在析构函数中包含处理代码,但后来因为种种原因被注释掉或者删除掉了,只留下一个空壳,此时应注意把析构函数本身注释掉或删除掉。1.1.3实现IDisposable接口垃圾回收事实上只支持托管内在的回收,对于其他的非托管资源,例如WindowGDI句柄或数据库连接,在析构函数中释放这些资源有很大问题。原因是垃圾回收依赖于内在紧张的情况,虽然数据库连接可能已濒临耗尽,但如果内存还很充足的话,垃圾回收是不会运行的。C#的IDisposable接口是一种显式释放资源的机制。通过提供using语句,还简化了使用方式(编译器自动生成try...finally块,并在finally块中调用Dispose方法)。对于申请非托管资源对象,应为其实现IDisposable接口,以保证资源一旦超出using语句范围,即得到及时释放。这对于构造健壮且性能优良的程序非常有意义!为防止对象的Dispose方法不被调用的情况发生,一般还要提供析构函数,两者调用一个处理资源释放的公共方法。同时,Dispose方法应调用System.GC.SuppressFinalize(this),告诉垃圾回收器无需再处理Finalize方法了。1.2String操作1.2.1使用StringBuilder做字符串连接String是不变类,使用+操作连接字符串将会导致创建一个新的字符串。如果字符串连接次数不是固定的,例如在一个循环中,则应该使用StringBuilder类来做字符串连接工作。因为StringBuilder内部有一个StringBuffer,连接操作不会每次分配新的字符串空间。只有当连接后的字符串超出Buffer大小时,才会申请新的Buffer空间。典型代码如下:StringBuildersb=newStringBuilder(256);for(inti=0;iResults.Count;i++){sb.Append(Results[i]);}如果连接次数是固定的并且只有几次,此时应该直接用+号连接,保持程序简洁易读。实际上,编译器已经做了优化,会依据加号次数调用不同参数个数的String.Concat方法。例如:Stringstr=str1+str2+str3+str4;会被编译为String.Concat(str1,str2,str3,str4)。该方法内部会计算总的String长度,仅分配一次,并不会如通常想象的那样分配三次。作为一个经验值,当字符串连接操作达到10次以上时,则应该使用StringBuilder。这里有一个细节应注意:StringBuilder内部Buffer的缺省值为16,这个值实在太小。按StringBuilder的使用场景,Buffer肯定得重新分配。经验值一般用256作为Buffer的初值。当然,如果能计算出最终生成字符串长度的话,则应该按这个值来设定Buffer的初值。使用newStringBuilder(256)就将Buffer的初始长度设为了256。1.2.2避免不必要的调用ToUpper或ToLower方法String是不变类,调用ToUpper或ToLower方法都会导致创建一个新的字符串。如果被频繁调用,将导致频繁创建字符串对象。这违背了前面讲到的“避免频繁创建对象”这一基本原则。例如,bool.Parse方法本身已经是忽略大小写的,调用时不要调用ToLower方法。另一个非常普遍的场景是字符串比较。高效的做法是使用Compare方法,这个方法可以做大小写忽略的比较,并且不会创建新字符串。例:conststringC_VALUE=COMPARE;if(String.Compare(sVariable,C_VALUE,true)==0){Console.Write(SAME);}还有一种情况是使用HashTable的时候,有时候无法保证传递key的大小写是否符合预期,往往会把key强制转换到大写或小写方法。实际上HashTable有不同的构造形式,完全支持采用忽略大小写的key:newHashTable(StringComparer.OrdinalIgnoreCase)。1.2.3最快的空串比较方法将String对象的Length属性与0比较是最快的方法:if(str.Length==0)其次是与String.Empty常量或空串比较:if(str==String.Empty)或if(str==)注:C#在编译时会将程序集中声明的所有字符串常量放到保留池中(internpool),相同常量不会重复分配。1.3多线程1.3.1线程同步线程同步是编写多线程程序需要首先考虑问题。C#为同步提供了Monitor、Mutex、AutoResetEvent和ManualResetEvent对象来分别包装Win32的临界区、互斥对象和事件对象这几种基础的同步机制。C#还提供了一个lock语句,方便使用,编译器会自动生成适当的Monitor.Enter和Monitor.Exit调用。1.3.1.1同步粒度同步粒度可以是整个方法,也可以是方法中某一段代码。为方法指定MethodImplOptions.Synchronized属性将标记对整个方法同步。例如:[MethodImpl(MethodImplOptions.Synchronized)]publicstaticSerialManagerGetInstance(){if(instance==null){instance=newSerialManager();}returninstance;}通常情况下,应减小同步的范围,使系统获得更好的性能。简单将整个方法标记为同步不是一个好主意,除非能确定方法中的每个代码都需要受同步保护。1.3.1.2同步策略使用lock进行同步,同步对象可以选择Type、this或为同步目的专门构造的成员变量。避免锁定Type★锁定Type对象会影响同一进程中所有AppDomain该类型的所有实例,这不仅可能导致严重的性能问题,还可能导致一些无法预期的行为。这是一个很不好的习惯。即便对于一个只包含static方法的类型,也应额外构造一个static的成员变量,让此成员变量作为锁定对象。避免锁定this锁定this会影响该实例的所有方法。假设对象obj有A和B两个方法,其中A方法使用lock(this)对方法中的某段代码设置同步保护。现在,因为某种原因,B方法也开始使用lock(this)来设置同步保护了,并且可能为了完全不同的目的。这样,A方法就被干扰了,其行为可能无法预知。所以,作为一种良好的习惯,建议避免使用lock(this)这种方式。使用为同步目的专门构造的成员变量这是推荐的做法。方式就是new一个object对象,该对象仅仅用于同步目的。如果有多个方法都需要同步,并且有不同的目的,那么就可以为些分别建立几个同步成员变量。1.3.1.4集合同步C#为各种集合类型提供了两种方便的同步机制:Synchronized包装器和SyncRoot属性。//Cr

1 / 39
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功