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