標籤:windows rt 記憶體 記憶體管理
很久不寫部落格了,不是因為不想寫,只是最近公司任務比較多,終於十一有時間出來冒泡了。今天繼續介紹移動開發中的重中之重——記憶體管理。C#代碼是Managed 程式碼,C# 程式員很少像C/CPP程式員那樣為程式資源的釋放而頭疼,一個C/CPP高手必須是記憶體管理的高手,作為C#程式雖然不要求像C/CPP程式員那樣管理記憶體資源,但是對記憶體機制還需要有深入的理解,那些代碼資源是託管資源交給GC去處理,那些資源需要程式員手工釋放,當然大家更關心的是非託管資源,因為託管資源GC會自動清理。
一般查看一個類是否是託管資源通常可以這樣做,在VS裡F12轉到定義,查看類或父類是否實現了IDisposable介面,所有實現了IDisposable介面的類都需要手動釋放資源,GC是不會清理這些資源的,在C#中實現了IDisposable介面常見的類有:資料庫連接父類DbConnection以及各種資料庫Adapter,Command等類,各種檔案流父類Stream等類。
對於這些類,我們有兩種策略。策略一: 在我們使用這些類的時候都用try...catch...finally語句,在finally裡面進行釋放資源。具體做法如下:
public class SqlHelper{//擷取設定檔中的資料庫連接字串 private static readonly string ConnStr = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; /// <summary> /// 支援預存程序的通用返回DataTable的資料庫參數查詢方法 /// </summary> /// <param name="sqlstr">查詢SQL字串</param> /// <param name="cmdtype">命令類型</param> /// <param name="paras">參數</param> /// <returns>DataTable結果集</returns> public static DataTable ExecuteDataTable(string sqlstr, CommandType cmdtype, params SqlParameter[] paras) { //建立實現IDisposable介面類對象 SqlDataAdapter adapter = null; try { adapter = new SqlDataAdapter(sqlstr, ConnStr); DataTable dt = new DataTable(); adapter.SelectCommand.CommandType = cmdtype; if (paras != null && paras.Length > 0) { adapter.SelectCommand.Parameters.AddRange(paras); } adapter.Fill(dt);//執行到此,adapter已經用完 return dt; } catch(Exception e) { //記錄錯誤記錄檔等操作return null; } finally {//釋放非託管資源 adapter.Dispose(); } }
這樣,當我們在調用SqlHelper的ExecuteDataTable方法的時候,我們就不必在關心Adapter對象資源的釋放,finally語句會在我們用完Adapter的時候自動將資源釋放,這樣雖然滿足了我們的要求,但是這樣似乎有點麻煩,好的。策略二: 那就用C#的using語句,具體如下:
public static DataTable ExecuteDataTable(string sqlstr, CommandType cmdtype, params SqlParameter[] paras) { //實現了IDisposable介面的類對象 using (SqlDataAdapter adapter = new SqlDataAdapter(sqlstr, ConnStr)) { DataTable dt = new DataTable(); adapter.SelectCommand.CommandType = cmdtype; if (paras != null && paras.Length > 0) { adapter.SelectCommand.Parameters.AddRange(paras); } adapter.Fill(dt); return dt; } }
這樣寫比用try..finally方便了許多,只要using實現IDisposable介面的對象,using語句塊走完IDisposable對象會被自動釋放,注意,沒有實現IDispoable介面的類是不能被using的,其實using內部也是try...catch...finally的實現,只是微軟給封裝好了,這也是C#程式員專有的文法糖。
當然,如果你也想實現IDisposable介面,能夠被using,那麼你就參考微軟MSDN建議的樣本就可以了,我在這裡就不多墨跡了。
說完非託管對象,該說託管對象,託管對象完全交由CLR的GC統一管理,那麼什麼時候GC會回收一下託管資源呢?
所謂託管資源,沒有實現IDisposable介面的一般類,沒有檔案和資料庫操作等,例如,int,string,List等,當我們在使用這些對象時,我們不需要關心他們的釋放,他們會由GC統一處理,一般如果系統記憶體夠用GC就不會回收這些託管資源,當記憶體緊張時GC會回收那些沒有任何引用指向的對象,當然C#也給程式員提供了手動調用GC回收的方法,GC.Collect()方法,但是即使程式員即使手動調用了該方法,GC也不一定在那一時刻對託管資源進行回收,另外Collect()方法不建議程式員手動的調用,如果頻繁調用會嚴重拖垮程式的效能,因為記憶體頻繁的回收,C#有三級記憶體回收,想瞭解的可以百度一下。
在網站的開發中,往往願意犧牲適當的記憶體來提高網站的效能,因為記憶體的存取速度遠遠超過磁碟的速度,這樣會緩解網站大並髮帶來的壓力,因此也產生了一批Memcached、Redis等伺服器記憶體管理軟體,甚至MySql資料庫也可以把資料存放區在記憶體中,當然伺服器重啟記憶體資料一般就無法恢複了(Redis可以恢複),因此應該把什麼資料放到記憶體中是開發的關鍵。
做為移動開發人員,對於記憶體的管理更是非常重要的。因為我們不像網站伺服器有那麼大的記憶體,目前來看,我聽說的記憶體最大的莫過於微軟的Surface Pro3,這是平板電腦的配置,但是如果是手機呢?最大的應該是小米的第4代手機3G記憶體,然後目前主流手機的記憶體應該在1.5G左右,蘋果的手機記憶體要更小,面對這麼小的記憶體,程式員開發時就一定要把握記憶體的使用。
既然手機記憶體那麼小,是不是我把所有的資源使用後就立馬釋放就好呢?這樣最節省資源啊?其實不是,這要根據具體的需求來定,有些情況下我們可以把一些常用的資源暫時放在記憶體中,等再次使用時從記憶體中調用可以大大提高程式的調用的速度,這裡我們可以藉助Framework的線程池原理,如果線程池裡有線程對象,就用線程池裡的對象,沒有再開啟一個新的線程。
這裡必須要說的一個重要的知識點即使弱引用,WeakReference類的原理是,將對象用WeakReference指向,然後將那個對象是置為null(被GC發現可以及時回收),我們在使用的時候直接使用WeakReference對象指向的對象就可以了,WeakReference會自動管理對象,當記憶體中有需要的對象,就直接使用,沒有就在記憶體中建立一個,具體使用方法如下:
object obj = new object();//對象由弱引用指向 WeakReference wref = new WeakReference( obj );//將對象置為null obj = null;//使用弱引用指向的對象object currObj=wref.Target;//使用currObj完成業務
關於弱引用就簡單的介紹到這裡,當然弱引用的使用也不僅僅只是這些,還有很多,大家可以自己搜尋一下,我就不過多介紹了,在後來的Windows Store應用開發裡我會具體的舉出弱引用的具體應用情境。如果想深入理解記憶體的使用,建議去網上看呂建中的設計模式視頻中的享元模式,享元模式講的是記憶體的共用,其實開發中很多東西都可以共用,深入理解池的概念很重要。
好了,今天就到這裡,我們下期見。
Windows移動開發(四)——閉關修鍊