程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> c#高性能編碼三

c#高性能編碼三

編輯:C#入門知識

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)   {                 Class objClassa=new Class();      objClassa.a=objClassb.b;      ...   }         6 循環次數一定的情況下,循環的出口條件不能使用函數之類的復雜語句   環的出口條件會進行多次判斷,直到滿足出口條件才退出,如使用函數等復雜語句,會造成多次進行比較復雜的函數運算等,影響性能。   正例:   int rowCount = db.rows.count;   for(int i=0; i< rowCount; i++)   {      ....   }       反例:   for(int i=0; i< myObject.GetItems().Count; i++)   {        ....   [csharp] view plaincopyprint?      }   7  異常處理   不捕獲自己無法處理的異常   說明:對於系統基礎的Exception異常等,捕獲到異常後,又不知道如何處理,一般就不去做try..catch操作。   反例1:   try   {           ….        }       catch       {   }   反例2:   try   {           ….        }       catch       {            thrownewException();   }   捕獲異常後,不進行任何操作。只能是把錯誤隱藏起來,同時影響性能。   8 避免在循環內部捕獲異常   說明:避免這樣做,最好將Catch范圍移到整個循環外部。   正例:   try   {       for (int i = 0; i < 1000; i++)       {   dt.rows[i] = …;       }   }   catch   {        …   }       反例:   for (int i = 0; i < 1000; i++)   {       try       {            dt.rows[i] = “”;       }       catch   {       …       }   }   9 采用 try catch finally  或者 using 塊 確保非托管資源的釋放   說明:using確保執行IDisposable接口的對象在退出塊時立即釋放,主要是為了防止忘記關閉數據庫連接可能導致的.net可執行程序的各種問題。   正例:   using (OracleConnection cn =new OracleConnection(connectionString))    {        OracleCommand cmd = new OracleCommand();            PrepareCommand(cmd, cn, commandType, commandText, commandParameters);            //create the DataAdapter & DataSet        OracleDataAdapter da = new OracleDataAdapter(cmd);        DataSet ds = new DataSet();            //fill the DataSet using default values for DataTable names, etc.        da.Fill(ds);            cmd.Parameters.Clear();            return ds.Tables[0];   }   9 線程  避免在asp.net裡面手工創建線程,太耗資源   如果確實需要,那就使用CLR thread pool   10  序列化 避免大量數據做序列化和反序列化操作   說明:序列化和反序列化操作消耗CPU資源。Asp.net編程中將對象保存到viewstate,需要做序列化操作,從viewstate獲取對象,又需要做反序列化操作。在web service調用時也需要序列化和反序列化,要注意web service的調用接口應盡量簡單   避免DataSet的序列化,使用實體列表替代DataSet後再進行序列化   說明:DataSet對象包含很多結構性的內容,先將數據集轉換成ILIST,序列化時消耗資源要少一些。   正例:   IList list =base.Select("",null);   ViewState["List"] = list;   反例:   DataSet ds =newDataSet();   ds = base.GetDataSet("","");   ViewState["Data"] = ds ;       11 反射   避免在循環和遞歸內使用反射   說明:反射消耗資源多,在循環和遞歸中多次反射時對性能影響大。   自定義類型裡實現 ToString來避免使用反射   說明:Object.ToString使用反射,在自定義類型時,自己實現 ToString方法,可以避免使用基類的ToString造成反射。       12 緩存   (1) 不存放太多變量在Caching裡面。   說明:在緩存中存放過多信息,緩存命中率低,造成緩存頻繁交換。       (2) 優先將變化很少的數據存放到緩存中   說明:在緩存中存放變化少的數據,多次使用,減少每次使用時需要從數據庫或配置文件獲取的資源消耗。對於使用不頻繁並且獲取不會消耗太多資源的數據不允許放在cache中。   13 Session   Session中不允許存放大數據對象,如DataTable、DataSet   SessionState 使用InProc方式時,Session其實是通過Cache方式存放在服務器的內存中的,所以如果當在Session裡面存放大量的數據時,容易導致W3WP進程的內存過高。使用StateServer或SQL Server方式時,每次請求都會序列化和反序列化   反例:   DataTable dt = GetData();   Session[“data”] = dt;   在頁面不需要session時,要把Session屏蔽掉   不需要時把此功能屏蔽掉     <sessionState mode="Off" />      <@% EnableSessionState = "false" %>   不需要修改session中的對象時,將頁面的EnableSessionState設成只讀的   不寫的話可以將EnableSessionState設成只讀提高並發   <%@ Page EnableSessionState="ReadOnly" %>       14  ViewState   ViewState中不允許存放大對象,如DataTable、DataSet   說明: ViewState對象中信息過多,會造成頁面過大,影響網絡傳輸。ViewState對象中存儲復雜對象,需要對象的序列化和反序列化過程,影響性能。   反例:   DataTable dt = GetData();   ViewState[“data”] = dt;   15 沒有動態數據顯示時,將ViewState功能關閉   說明:根據頁面及控件的具體情況,不需要ViewState信息時,設置EnableViewSate為False   16 數據分頁   對於返回超過1000行數據的查詢,不用DataGrid的分頁功能,而應該在查詢數據時分頁,只取1頁數據到前端。   說明:將數據從數據庫全部取回來後在ASP.NET裡面使用DataGrid的分頁功能來實現. 對於返回數據量比較大的查詢,這樣做會帶來兩方面的問題:1、消耗應用服務器較多的內存,因為需要生成很大的DataTable;2、消耗應用服務器較多的CPU,因為需要在很大的DataTable裡面選擇需要輸出的數據頁。   對於Oracle 數據庫,通過SQL 語句裡面的ROWNUM可以實現分頁。   正例:   使用此SQL,獲取第81到100共計20行數據,到前端作為1頁顯示:   SELECT * FROM   (   SELECT A.*, ROWNUM RN   FROM (SELECT * FROM TABLE_NAME) A   WHERE ROWNUM <= 100   )   WHERE RN >= 81   反例:   使用SELECT * FROM TABLE_NAME獲取到所有數據,綁定到DataGrid,依賴DataGrid來分頁。   17 客戶端腳本   使用客戶端腳本判斷用戶輸入   說明:用戶輸入的必填項等在客戶端先判斷一遍,服務器端再校驗一次,減少服務端判斷不通過時頁面回傳的情況。       18 頁面   (1)避免使用Page.DataBind   說明:Page.DataBind遞歸在頁面的每個控件上調用DataBind。   (2)避免使用DataBinder.Eval   說明:DataBinder.Eval使用反射   正例:   <%# ((DataRowView)Container.DataItem)["field1"] %>   反例:   <%# DataBinder.Eval(Container.DataItem,"field1") %>   避免頁面過大   功能設計時,不宜在頁面上堆積太多內容,大頁面的網絡傳輸耗時   web應用發布時建議對js文件進行壓縮   說明:使用js壓縮工具處理後的js文件,文件大小只有原是文件的1半左右,能減少用戶請求頁面的數據傳輸量。            

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved