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半左右,能減少用戶請求頁面的數據傳輸量。