ArcEngine中Com对象释放的技巧

声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
欢迎加群: GIS开发部落

缘起

AE开发中经常会和Com对象打交道,合理的释放Com对象才能使程序运行的稳定快速。Com对象不及时释放可能会引发以下问题

  • 内存占用高,极端情况引发内存异常
  • 程序异常
  • 数据锁定,如表锁定、镶嵌数据集锁定、文件锁定等问题
  • 无法删除文件(如shapefile)
  • 无法删除FeatureClass,无法修改表结构

本文主要总结如何释放Com对象,以及释放Com对象的技巧,学会如何释放Com对象是一道必备的技能,总结一下,For Me And For You,希望你也能写出不令人唾弃的代码。

常见要释放的Com对象

游标对象(如IFeautreCursor、ICursor)、Geodatabase对象(如IWorkspace、IDataset、IFeatureClass、ITable、IRaster、IRasterDataset、IMosaciDataset等)、StyleGallery对象等。

如果你不清楚哪个是com对象,该不该释放,可以使用System.Runtime.InteropServices.Marshal.IsComObject方法判断一个对象是不是Com对象。
参考传送门:ESRI.ArcGIS.Geoprocessor.Geoprocessor unable to release memory

Com对象释放的几种方法

ArcEngine帮助参考位置
在这里插入图片描述

AOUninitialize.Shutdown

在这里插入图片描述
有时候你开发的独立的程序关闭的时候会报一个意想不到的错误,当你退出一个加载了MapControl的ArcEngine程序时你也许会收到类似如下的错误,The instruction x references memory at x. The memory could not be read.
当COM对象在内存中保留的时间超过预期时,可能会发生这些错误,从而阻止COM库在进程关闭时从进程中正确卸载。为了帮助防止这些错误,添加ESRI.ArcGIS.ADF.Local引用,添加一个静态的Shutdown函数,此函数通过确保在进程关闭之前卸载未使用的COM引用,有助于避免这些错误。

ESRI.ArcGIS.ADF.COMSupport.AOUninitialize.Shutdown()

此函数只帮助卸载没有COM对象的库。在处理完任何COM对象后应用此函数更为有益。例如,在带有启动窗体的Arcgis Engine for Windows应用程序中,将对aoUninitialize.shutdown的调用放在窗体释放的事件处理程序中。

ComReleaser

使用前添加ESRI.ArcGIS.ADF.Connection.Local.dll ,常见 comReleaser.ManageLifetime方法管理Com对象,如下是简单的示例代码:

using (ComReleaser comReleaser = new ComReleaser())
{
    var comObj1=......;//伪代码
    comReleaser.ManageLifetime(comObj1);
    ......
    var comObj2=......;//伪代码
    comReleaser.ManageLifetime(comObj2);
    ......
}

使用ComReleaser管理Com对象,当Using语句执行完毕,所有的Com对象即会被释放,建议Com对象管理的代码和对象放到一起,这样无论是修改代码还是检查代码都比较简单。

Marshal.ReleaseComObject

需要在代码文件的开头添加类型引用 Using System.Runtime.InteropServices.Marshal ,一般使用 Marshal.ReleaseComObject方法释放Com对象,使用示例如下:

private void MyFunction()
{    
	ESRI.ArcGIS.Display.IStyleGallery styCls = new	ESRI.ArcGIS.Framework.StyleGalleryClass()as	ESRI.ArcGIS.Display.IStyleGallery;    // Use the StyleGalleryClass here.
    int refsLeft = 0;
    do
	{
        refsLeft = Marshal.ReleaseComObject(styCls);
	}
    while (refsLeft > 0);
}

Com对象释放注意事项

  1. 优先使用ComReleaser管理Com对象
  2. 在使用Com对象的时候,最好就管理起来,避免遗漏
  3. 循环中产生的Com对象,建议及时回收,不要等循环完毕一次性回收
  4. 函数生成的Com对象也要释放,避免遗漏(如使用ISaveAs接口保存图片的时候)

Com对象释放常见错误

以下是我项目实际开发中遇到的各种坑爹代码,建议阅读以下,避免写出惨绝人寰的代码。(Ps:以下示例代码均为伪代码)

  • 释放的目标对象选择错误,比如对Null对象释放

    Using(ComReleaser = new ComReleaser())
    {
    	IFeatureCursor pFeaCursor=pFeatureClass.Search(null,true);
    	//错误写法
    	IFeature pFeature = null;
    	pComReleaser.ManageLifetime(pFeature);//类似这种的代码,对Null对象上管理,基本无用
    	pFeature=pFeaCursor.NextFeature();
    	//正确写法
    	IFeature pFeature=pFeaCursor.NextFeature();
    	if(pFeature!=null)
    	{
    		pComReleaser.ManageLifetime(pFeature);//要管理非空的对象才有意义
    		......
    	}	
    }
    
  • 对同一个对象或全局对象多次释放(这种问题一般在IWorkspaceFactory IWorkspace IFeatureClass上比较常见,建议大家了解一下单例模式 和 ArcEngine中资源池的概念)

    //定义一个IWorkspace对象(可以是全局对象或者单例对象,不懂单例的可以百度单例模式)
    IWorkspace pWs = pWsFactory.OpenWorkspace(wsPath,0);
    //用一个Workspace同时打开两次要素类,得到的其实是同一个要素类引用
    IFeatureClass pFeaClass1 = (pWs as IFeatureWorkspace).OpenFeatureClass(tableName);
    IFeatureClass pFeaClass2 = (pWs as IFeatureWorkspace).OpenFeatureClass(tableName);
    //释放其中的一个要素类
    Marshal.ReleaseComObject(pFeaClass1);
    //再次调用另外一个要素类会报错
    var name=(pFeaClass2 as IDataSet).Name;
    
  • 函数产生的临时对象忽略释放

    IDataset pDataset=pSaveAs.Save(参数1,参数2,参数3);
    //切记要释放SaveAs产生的临时对象
    Marshal.ReleaseComObject(pDataset);
    
  • 循环中的Com对象也要及时释放

    for(int i=0;i<10000;i++)
    {
    	IRow pRow=pTable.CreateRow();
    	......
    	//测试的Pow使用完毕记得释放
    	Marshal.ReleaseComObject(pRow);
    }
    
    • IGeometry对象使用完,记得使用SetEmtpy方法释放内存
      Use the SetEmpty method to clear geometries and release memory. For example, a polygon with 100 rings will have an internal array of 100 pointers to ring objects. That array will go away and Release will be called on each ring. If that polygon had the only reference on those rings, then they’ll go away, which releases all their segments, which may also then go away.

      private bool EmptyGeometry(IGeometry geometry)
      {
          geometry.SetEmpty();
          return geometry.IsEmpty;
      } 
      

其他说明

GC.Collect 也是回收内存的一种方法,另外开发SOE程序时不建议使用GC.WaitForPendingFinalizers(),经测试发现GC.WaitForPendingFinalizers()会长时间挂起程序,不过本地winform开发还是可以使用的。

练习

  1. 利用游标查询数据,管理其中的Com对象。
  2. 另存栅格数据,管理生成的临时Com对象。
  3. 手写一个打开Shapefile,然后做简单的属性查询,体验Com对象的管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值