.NET內存管理的最佳實踐
譯文【51CTO精選譯文】我們在實際編程中使用的內存往往都會超出程序需要的內存,對于桌面應用程序內存是相對廉價的,但如果你在開發ASP.NET應用程序,需要處理服務器上大量的內存時,過度使用內存可能會帶來很多痛苦,因此有必要討論一下.NET內存管理的***實踐,以減少內存浪費。
程序員在為類中的成員變量獲取內存時往往有些多余的行為,因為有些不必要的內存使用會浪費掉一些內存空間,我們來看一段代碼:
- public class BadUse
- {
- private SqlConnection con = new SqlConnection();
- private DataSet ds = new DataSet("MyData");
- public BadUse() {}
- public BadUse(string connectionString)
- {
- SqlConnection = new SqlConnection(connectionString);
- }
- public BadUse(SqlConnection con)
- {
- this.con = con;
- }
- }
如果在我們的系統中使用了多余的內存,類在被調用之前,甚至是構造函數被調用之前,就調用了對象成員初始化程序,它將為所有成員變量獲取內存,在上面的代碼中,在初始化期間我們創建了一個SqlConnection對象,在那之后,我們都調用默認的構造函數或創建對象的構造函數,因此沒有使用已經創建好的對象,重新創建了一遍對象,因此浪費了內存空間。
.NET內存管理***實踐方法:
- public class GoodUse
- {
- private SqlConnection con = null;
- private DataSet ds = null;
- public SqlConnection Connection // Better to use Properties
- {
- get
- {
- if(this.con == null) // Always check whether there is an existing object assigned to member
- this.con = new SqlConnection();
- return this.con;
- }
- set
- {
- if(value == null || this.con !=null)
- {
- this.con.dispose(); // Clears out Existing object if member is assigned to Null
- this.con = null; // Always better to assign null to member variables
- }
- if(value !=null) this.con = value;
- }
- }
- public GoodUse() {}
- public GoodUse(string connectionString)
- {
- this.Connection = new SqlConnection(connectionString); //Assignes new object to null member
- }
- public GoodUse(SqlConnection con)
- {
- this.con = con;
- }
- }
從上面的代碼我們就更清晰了,使用屬性總比直接訪問對象要好,這里還給了你一個接口方便以后修改每個調用,與此類似,使用事件訪問程序訪問事件總是***的。
- private MyDelegate MyEvent;
- public MyDelegate CheckEvent
- {
- add
- {
- MyEvent + =value;
- }
- remove
- {
- MyEvent -= value;
- }
- }
在VB.NET中可以使用RaiseEvent,在代碼中無論何時觸發了事件都會調用它。
使用Using和Try/Catch程序塊
使用可隨意使用的對象時***使用Using程序塊,在.Net提供的所有構造函數中,Try /Catch和Using程序塊經常調用Dispose()函數,只要對象實施了IDisposable,因此在.Net中使用Try /Catch和Using程序塊總是***的。來看下面的代碼:
- public void Execute(string connectionstring, string sql)
- {
- SqlConnection con = new SqlConnection(connectionstring);
- SqlCommand cmd = new SqlCommand(sql, con);
- con.Open();
- cmd.ExecuteNonQuery();
- cmd.Dispose();
- con.Dispose();
- }
在上面的代碼片段中,我們簡單地創建了SqlConnection和SqlCommand對象,這兩個對象都實施了IDisposable,因此上面的代碼可以按照***實踐方法重寫如下:
- public void Execute(string connectionstring, string sql)
- {
- using(SqlConnection con = new SqlConnection(connectionstring))
- {
- using(SqlCommand cmd = new SqlCommand(sql, con))
- {
- con.Open();
- cmd.ExecuteNonQuery();
- }
- }
- }
這樣重寫之后將會自動調用Dispose函數,我們不需要直接調用它,因此對于快速資源解除分配***使用Using程序塊。
也可以使用Try/ Catch程序塊,如:
- try
- {
- SqlConnection con = new SqlConnection(connectionstring);
- try
- {
- SqlCommand cmd = new SqlCommand(sql, con);
- con.Open();
- cmd.ExecuteNonQuery();
- }
- catch {}
- finally
- {
- cmd.Dispose();
- }
- }
- catch(){}
- finally
- {
- con.Dispose();
- }
- }
接著是使用as或is比使用強制類型轉換要好,也就是說如果我們想轉換一個類型,應該使用as關鍵字而不是使用明確的類型映射。
- object o = new SqlConnection();
- SqlConnection con = o as SqlConnection; // Better to use this
- SqlConnection con = CType(o, SqlConnection); // Not always better
在上面的語句中,如果你使用第二個轉換語句,如果CType不能轉換成那個類型或在o中有空值,它將會拋出錯誤,但在使用了as的語句中不會拋出錯誤,而是使轉換無效。
調用函數時使用Structure
調用函數時參數少是一件好事,如果直接向函數發送一個大對象將會花大量的時間發送多個參數,此時可以為那些要發送的參數創建一個Structure,然后直接發送這個Structure。
- public void Callme(int x, int y, string zy)
- public void Callme(argumentStruct st) // Better in performance
發送一個Structure總比發送離散對象要好。
使用一個大型組件總比使用大量的小型組件要好,大型組件中有許多命名空間,而不是大量的小型類庫,微軟也是這么干的,它將所有的組件都創建在mscorlib.dll中了,減少了元數據的負荷、JIT編譯時間和安全檢查等。
如果不是必須的***避免使用線程。
通常情況下,使用大量的線程可能導致性能下降,因為每個線程在獨立運行時會消耗大量的內存,當你需要快速處理時可以使用線程,但它會增加內存消耗。
當你創建線程時使用ThreadPool(線程池)。
避免使用ArrayList和HashTables,當你需要插入隨機數據時可以使用鏈接數組。
如果你看到ArrayList或HashTables的內部結構,就知道它僅僅是數組的打包,無論何時向這些結構中插入對象時,它將為所有已分配量重新聲明,然后手動轉變它們,ArrayList是一個對象數組,HashTable是一個結構數組。
另一個奇怪的事情是,ArrayList或HashTables的區間系數是4,這意味著它無論何時需要內存時分配的內存總是4的倍數,當你需要隨機插入時,LinkLists、Generic Lists和LinkedArrays比Collections對象的性能要好,當你需要添加數據和按順序顯示數據時使用Collections更好。
原文:Best Practices of Memory Usage
作者:Abhishek Sur
【編輯推薦】