C#+ArcEngine中com对象的释放问题
1、问题描述
最近在写C#下AE的开发,在循环获取数据并修改时碰到了两个问题“超出系统资源”和“超出打开游标最大数”;在网上看了一些资料,发现都是说在循环中没有释放已经使用过的对象,但是在循环中实际上是有为com对象赋值为null的,但是还是没法解决。后来想着将对象赋值为null和marshal是不是效果不一样,就特意写了一个简单的循环来测试,代码如下(初级代码,比较乱,请轻喷):
public void Test_释放游标方式()
{
string ssName = "controlpoint";//图层名称(里面有4w多条数据)
//测试两种释放方式的执行时间
DateTime tmStart = DateTime.Now;
DisplayString("开始于:"+tmStart.ToLongTimeString()+"\r\n");
DateTime tmEnd;
DateTime tmMiddle1;
DateTime tmMiddle2;
TimeSpan ts, tsMax;
//具体执行代码
IFeatureWorkspace pFeaWs = mc_Ws as IFeatureWorkspace;//我已经建立好的SDE工作空间对象
IFeatureClass pFeaCls = pFeaWs.OpenFeatureClass(ssName);
string strWhere = string.Format("stationserieseventid in ('f9bd16ed-ae2a-454c-9eba-7123dc41af28','7e3d0d4a-8c5e-49b5-8977-e060cd4cef6d','a89300a5-3503-4976-b5d2-3d5a712f7b36')");
IFeatureCursor pCur = null;
IFeature pFea = null;
IQueryFilter pFilter = new QueryFilterClass();
int numCurHasBuild = ;
//int counsts = pFeaCls.FeatureCount(null);//总要素个数
try
{
tmStart = DateTime.Now;
DisplayString("开始于:" + tmStart.ToLongTimeString()+"\r\n");
tsMax = TimeSpan.MinValue;
int idxStart = ;
int idxEnd = ;
int idxTmpNode = idxStart +
for (int idx = idxStart; idx < idxTmpNode; idx++)
{
tmMiddle1 = DateTime.Now;
strWhere = "objectid = '" + idx.ToString() + "'";
pFilter.WhereClause = strWhere;
//获取游标对象
pCur = pFeaCls.Search(pFilter, false);//如果游标对象没有释放,那么一次循环不能超过280,否则会爆‘超出打开游标最大数’错误
numCurHasBuild++;
//循环获取游标内的要素
pFea = pCur.NextFeature();
while (pFea != null)
{
string tmp = pFea.get_Value().ToString();
pFea = pCur.NextFeature();
}
//pCur = null;//像这样,对象实际上是没有释放的;依旧会在283条的时候报错
Marshal.ReleaseComObject(pCur);//这种方式可以完全释放掉对象,此时可以完全循环完4w条数据
localReleaseComObj(pCur);//自己写的一个方法,达到释放游标pCur的目的
if (pCur != null)
localReleaseComObj(pCur); tmMiddle2 = DateTime.Now;
ts = tmMiddle2 - tmMiddle1;
if (ts > tsMax)
tsMax = ts;
}
tmEnd = DateTime.Now;
DisplayString("循环中耗时最多的一次时间为:"+tsMax.TotalSeconds+"\r\n");
DisplayString("执行完一轮循环;消耗的总时间为:"+(tmEnd-tmStart).TotalSeconds+"\r\n");
}
catch (Exception ex)
{
DisplayString("在第" + numCurHasBuild.ToString() + "处发生错误!\r\n" + ex.Message);
throw new Exception(ex.Message);
}
}
2、试验过程
在测试中,专门打开一个控制点图层(里面有4w多条数据),然后根据条件循环建立游标获取对象;
情况一:在使用完游标之后,将其赋值为null;这时候对象实际上是没有被释放掉的,因此在283次循环时就会报错‘ora 超出打开游标最大数’;
情况二:在使用完游标时,在循环末尾,用marshal.releasecomobject方法来释放com对象,这时循环可以走完4w次;我认为这时对象是完全被释放掉了的。
同时在使用marshal.releasecomobject方法时,并没有增加多余的时间,循环执行时间上还是跟不释放对象一样(小数据量比较)。
3、结论
在使用ArcEngine中的游标对象时,一定要在使用完之后进行对象的释放,否则会不定时出现上面的错误;而且需要使用marshal.releasecomobject方法来进行对象的释放,赋值为null是达不到目的的;
另外说一下“超出系统资源”的这个错误,这个在最初没有释放对象时,每次当数据量超过200就会不定时报这个错误,一直也没法解决;但是当我在循环中加上marshal.releasecomobject方法释放对象后,这个问题竟然莫名消失了(后来特意试了4000条数据,也可以通过测试)。相当于解决“超出打开游标最大数”的问题时,顺带着让另一个问题也消灭了(可能是我不理解其间的具体原理,有待深入)。
到这里,ArcEngine对象释放问题已经阐述完毕。以下是在解决过程中碰到的另一个问题,看官可以选择性忽略。
Other-补充问题(问题3:“COM对象与其基础RCW分开后就不能再使用”)
如代码中黄色部分所示,在这里我碰到了一个很奇怪的问题,即在上面一句我已经用Marshal释放了pCur对象,但是后面仍然可以继续使用(指的是再次用Marshal释放)而不报错。在这里我是将pCur作为参数传递进方法内部,然后再次使用Marshal释放,我一共尝试了以下几种方式:
①普通的参数传递:可以正常使用,并Marshal而不报错;
②ref参数:也可以正常使用并Marshal而不报错
③params参数:此时不能正常使用,在调用方法时就会报错“com对象与其基础rcw分开后不能再使用”,而且连方法内部都进不去。
经过一天的查找原因,我认为原因如下:
首先,params是数组型参数,我们一般是想达到分散传递任意个数的参数的目的;此时,我们看似传递的是一个个的参数,但是编译器在进入方法之前会做一项工作,即使用我们传递的这些分散的参数建立一个一维数组,然后将这个数组传递给方法进行后续的操作(注意:错误也就是在这一步出现,就是我们连方法都进不去就报错的原因)。
其次,因为C#中数组是引用类型,在建立数组时是同时进行堆和栈上的内存分配的,也即数组的实际内容是在堆上存储,而在栈上开辟一块内存用于保存数组名(即堆中保存实际数据的内存的地址)。这就是C#中引用类型变量建立的方式:引用类型的地址存放在栈上,而实际对象存储在堆上。
第三,ArcEngine的com对象建立就遵循上面的原则,及同时使用了堆和栈(这部分由.NET的rcw为我们做了,具体原理现在还不清楚);因此我们在C#实际使用ArcEngine的对象时就像普通的引用类对象一样。而当我们使用Marshal将建立的对象释放后(比如IFeatureCursor),相当于斩断了堆和栈之间的联系桥梁;
第四,而我们在Marshal过pCur对象一次之后,再像上面黄色代码的那一部分来调用pCur时,普通参数、ref参数都不会去寻找pCur所对应的在堆上的对象(形象一点就是都不会走这一段已经被斩断了的桥梁);但是params数组型参数不同,编译器在进入方法之前是需要进行一步数组建立的操作的,而在建立数组时是需要同时操作堆和栈的,这时候再去走那一段已经被斩断了的桥梁就无法通过,也就会报错“com与其基础rcw分开后不能再使用”。【这一步你也可以变换一种方式测试:即建立一个直接以数组作为参数的方法,然后使用时事先建立数组,再把数组作为参数传递。这时候你会发现,数组是无法建立成功的,报错就出现在这里】
结论:这里params数组参数的语法糖给我们了一个小坑,如果不去理解堆、栈的概念,还有方法的参数传递与使用,就会觉得这个错误报的莫名其妙;而当深入理解了C#中方法的参数类型、传递,以及引用类型对象的原理,就能理解这个错误。【以上只是我个人查找资料后的理解,总感觉理解了出错的原因,但是没法清楚的表达出来;还有关于最后堆、栈的问题,我理解的很浅,如果有不对的地方希望各位大神能指出来,我再修改】
C#+ArcEngine中com对象的释放问题的更多相关文章
- ArcEngine中COM对象与其基础RCW分开后就不能再使用
操作ArcEngine中的COM对象时,为了减少内存的增长,用掉的对象要手动释放常用的方法是ReleaseComObject System.Runtime.InteropServices.Marsha ...
- Session中清除对象方法比较
转载. https://blog.csdn.net/u014401141/article/details/51816308 Session中清除对象方法比较 http://blog.csdn.ne ...
- EasyDarwin开源流媒体服务器中一种实现对作用域内new对象自动释放的方法(值得借鉴)
我们经常在开发过程中,在局部new了一个对象,我们就会在复杂的逻辑过程中担心这个对象是否已经被释放,需要在一大堆的if.else.while.break进行判断new对象是否还存在,或者相同的dele ...
- ASP.NET中application对象
ASP.NET中application对象的使用. Application对象的应用 1.使用Application对象保存信息 (1).使用Application对象保存信息 Applicat ...
- java 哪些情况下会使对象锁释放
Java_多线程_锁释放 问:Java多线程运行环境中,在哪些情况下会使对象锁释放?答:由于等待一个锁的线程只有在获得这把锁之后,才能恢复运行,所以让持有锁的线程在不再需要锁的时候及时释放锁是很重要的 ...
- GDI 对象的释放与内存泄漏的问题研究
最近写了一个GDI 绘图的程序,过程中遇到一个奇怪的问题,就是 定时器定时一会GDI绘的图就消失了..后来经过分析,原来是 GDI对象数量过多 ,即GDI对象超过10000个 导致内存泄漏的问题.找到 ...
- ORACLE Recyclebin管理及flashback recyclebin中的对象
Flashback用于恢复用户误删除的对象(包括表,索引等), 不支持sys用户. system表空间下的对象,也不能从回收站里拿到.故使用SYS 或者SYSTEM用户登陆时, show recycl ...
- ASP.NET中application对象的用法(面试题)
ASP.NET中application对象的用法 本文导读:Application对象是HttpApplicationState类的一个实例,Application状态是整个应用程序全局的.Appli ...
- JVM中判断对象是否存活的方法
Java中几乎所有的对象实例都存放在堆中,在垃圾收集器对堆内存进行回收前,第一件事情就是要确定哪些对象还“存活”,哪些对象已经“死去”(即不可能再通过任何途径被使用). 引用计数算法 首先需要声明,至 ...
随机推荐
- Simditor使用方法
一不小心接触到Simditor,瞬间被它优美极简的界面所吸引.Simditor是Tower开源的所见即所得的在线富文本编辑器. Simditor的理念是保持简单,避免过度的功能,每一个特性都追求极致的 ...
- Httphelper工具1
[苏飞开发助手V1.0测试版]官方教程与升级报告导读部分------------------------------------------------------------------------ ...
- PHP生成缩略图函数
function img_create_small($big_img, $width, $height, $small_img) { // 大图文件地址,缩略宽,缩略高,小图地址$imgage = g ...
- 谷歌、flick网站图片 一次性下载 javaWeb项目 多线程下载,
源码下载:http://download.csdn.net/detail/liangrui1988/5760473
- java 逆波兰表达式
最近想把这破机 装成WIN7 想想还是算了 ... 反正用的机会也不多. 不过 发现了一些 想念的东西 从前的作业. 从前的记忆. package org.lmz; import java.util ...
- VS2010 boost 使用问题一例
最近有一个VS2010的工程用到了boost库,编译的时候报下面的错误: >LINK : fatal error LNK1104: cannot open file 'libboost_thre ...
- 通告机制Notification
Obj-c的基本通讯原则是对象间的消息传递,这种情况多出现在两个对象之间.但是如果多个对象共同关注一个对象状态的时候呢,当然可以让发生事件的对象向所有关注他的对象发送消息,但是这并不高效.所以有了通告 ...
- JVM参数设置、分析
不管是YGC还是Full GC,GC过程中都会对导致程序运行中中断,正确的选择不同的GC策略,调整JVM.GC的参数,可以极大的减少由于GC工作,而导致的程序运行中断方面的问题,进而适当的提高Java ...
- 用php 进行对文件的操作 (上)
如何让自己磁盘中的文件夹和目录显示在网页上?那就来看一下,用php是怎么来操作他们的吧 php中文件,一般包含两块内容,文件和目录 先来一句一句的看代码,及他的作用 运行后看一下结果 file 指的是 ...
- MySQL分表
一.概念 1.为什么要分表和分区?日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询 ...