.NET+AE開發中常見幾種非託管對象的釋放 我們知道.Net中對於記憶體的管理有兩種方式,一種是託管對象管理,另一種是非託管對象管理。而我們通常理解的記憶體管理就是GC(垃圾收集),雖然GC通過對託管堆的管理,可以使我們有機會從繁鎖的諸如記憶體流失之類的問題中解放出來,可以將精力專註於程式的邏輯上。但是將所有的事情都交給GC有時會損及程式的效率,嚴重的甚至會導致錯誤。 為什麼會出現這種情況呢?問題在於對非託管資源(檔案控制代碼)或者需要特別關照的對象(Bitmap)對象等,GC表現得就有點差強人意了(這句話或許說得並不正確,因為微軟設計GC的本意就是用來針對託管對象管理)。如何有效地利用GC來進行記憶體管理,如何對程式的效能進行最佳化不是本文討論的範圍。本文的要旨在於AE開發中如何來釋放非託管對象。 一、AOUninitialize.Shutdown. 很多時候我們都會遇到這種情況:在退出AE應用程式中,常常提示這樣的錯誤:“The instruction x references memory at x. The memory could not be read”。出現這種錯誤的原因主要在於COM對象(非託管對象)沒有釋放,在我們結束使用它的進程的時候,它阻止我們正常釋放它,它釋放的優先順序高於當前使用它的進程釋放的優先順序,也就是說進程釋放之前,必須先釋放掉它。 明白了上面的原理,那麼解決這一問題的方法便非常簡單,我們只需在應用程式退出之前調用ESRI.ArcGIS.ADF.COMSupport.AOUninitialize.Shutdown()方法即可釋放非託管對象了(9.2之前的版本AoUninitialize並不在ESRI.ArcGIS.ADF.COMSupport命名空間下,需注意)。 二、Marshal.ReleaseComObject NET開發中,引用COM對象主要是通過RCW(Runtime Callable Wrappers)機制來實現的(有點類似於代理模式)。對於COM對象的釋放,GC表現得有點無能為力,因此必須在程式中顯示釋放掉COM對象佔用的資源,否則將會出現一些意想不到的錯誤。比如重複地從Personal GeoDataBase中開啟GeoDataBase Cursors而又沒有及時釋放,將會引發“No more tables can be opened.”其它情形中,你可能會發現應用程式退出時,COM對象依然在記憶體中引用。比如StyleGallery如果沒有顯示釋放,在應用程式退出時就會引發錯誤。 1、Releasing StyleGallery: private void MyFunction() { IStyleGallery styCls = new StyleGalleryClass() as IStyleGallery; // Use the StyleGalleryClass here ... int refsLeft = 0; do { refsLeft = Marshal.ReleaseComObject(styCls); } while (refsLeft > 0); } 2、Releasing geodatabase cursors for (int i = 1; i < 2500; i++) { IQueryFilter qu = New QueryFilterClass(); qu.WhereClause = @"Area = " + i.ToString(); IFeatureCursor featCursor = featClass.Search(qu, true); // Use the feature cursor as required System.Runtime.InteropServices.Marshal.ReleaseComObject(featCursor); } 3、Releasing WebObject(ArcGIS Server) private void doSomthing_Click(object sender, System.EventArgs e) { using (WebObject webobj = new WebObject()) { ServerConnection serverConn = new ServerConnection("doug", true); IServerObjectManager som = serverConn.ServerObjectManager; IServerContext ctx = som.CreateServerContext("Yellowstone","MapServer"); IMapServer mapsrv = ctx.ServerObject as IMapServer; IMapServerObjects mapo = mapsrv as IMapServerObjects; IMap map = mapo.get_Map(mapsrv.DefaultMapName); IFeatureLayer flayer = map.get_Layer(0) as IFeatureLayer; IFeatureClass fclass = flayer.FeatureClass; IFeatureCursor fcursor = fclass.Search(null, true); webobj.ManageLifetime(fcursor); IFeature f = null; while ((f = fcursor.NextFeature()) != null) { // do something with the feature } ctx.ReleaseContext(); } } 三、什麼時候應該釋放非託管對象? 這個問題非常重要,當我們確信在釋放之後不再調用非託管對象資源時候,就可以調用System.Runtime.InteropServices.Marshal.ReleaseComObject()方法進行顯式釋放了,否則將會引發新的錯誤。 |