.Net环境下调用COM组件,垃圾回收的机制和原则?
.net的托管并不是万能的,对于有些资源如窗体、文件、位图、数据库连接都需要相应的手动回收。
.net使用的托管内存,值类型存储在堆栈上,引用类型存储在托管堆上,由GC负责垃圾回收。而COM对象使用的是内置内存,因此无法托管,需要手动释放内存。但是COM的内存管理机制是怎么样的呢?.net环境下调用COM组件,COM对象的垃圾回收应该如何进行呢,一般原则又是什么呢?这些我都不知道。
于是在ArcGIS Engine论坛上发帖求助,也没有人回答。现在把遇到的问题重新整理一下,发到博客园,希望能够得到解答。不管是自己还是别人帮助。也记录这个过程。一共发了三个帖子,如下:
1.AE进行二次开发中,COM对象的垃圾收集问题应该如何进行?
问:AE进行二次开发中,经常忽略的垃圾收集问题,AE是COM对象,垃圾收集问题应该如何进行?一般的for循环中的COM对象在什么时候释放了,还是自动释放?其他的情况应该注意哪些? |
回答:我一般用Marshal.ReleaseComObject(XXX)在循环后释放部分com对象。好像还有方法可以强制进行垃圾回收,不过那样做代价太大 |
private void btnAddMap_Click(object sender, EventArgs e)
{
IMapDocument pMapDoc = new MapDocumentClass();//MapDocumentClass是COM对象,后记:此说法错误
pMapDoc.Open("D:\\演示数据\\专题[url=file://\\Untitled.mxd]\\Untitled.mxd[/url]", "");
IMap pMap = pMapDoc.get_Map(0);//返回COM对象MapClass的接口IMap 后记:COM对象Map对应RCW的接口IMAP
axMapControl1.Map = pMap;
Marshal.ReleaseComObject(pMapDoc); //(1)
Marshal.ReleaseComObject(pMap); //(2)
GC.Collect(); //(3)
axMapControl1.Refresh();
}
(1)(2)(3)句代码做如下组合,a类三句代码都不添加,b添加(1)(2),c添加(3),d添加(1)(2)(3)句代码。对每种组合重复点击button按钮,这样Map就会重复加载,每次都会有MapDocumentClass和pMapDoc.get_Map(0)产生新的COM对象,第一次加载内存增长比较多可以理解。a类加载到21次左右,程序弹出错误如图(1);b种不弹出错误,点击40次没有报错,此时内存还是不断往上涨的;C中内存没有b中增长的那么迅速,在点击20次左右的时候似乎基本稳定了;d中内存增长最小,在20次左右也基本恒定了。我的数据中主要是一些Tin和一个GeodataBase中的一些要素(图2),整个数据集大小5M左右,地图文档749K;
这样看来只有是New的COM对象,只要没有继续引用就应该释放比较好啊!
问:依然是前面的程序,通过如下两句返回当前COM对象释放一次后的引用数n,m。 采用如下代码:IMapDocument pMapDoc = new MapDocumentClass(); |
以上测试了重复加载Map,出现的一些症状。后来发现自己忽略了MapDocument的Colse方法。这个方法是不是释放文件资源呢?
继续测试如下代码:
private void btnAddMap_Click(object sender, EventArgs e)
{
IMapDocument pMapDoc = new MapDocumentClass();
pMapDoc.Open("D:\\演示数据\\专题[url=file://\\Untitled.mxd]\\Untitled.mxd]\\Untitled.mxd]\\Untitled.mxd[/url]", "");
IMap pMap = pMapDoc.get_Map(0);
pMapDoc.Close();
axMapControl1.Map = pMap;
int n = Marshal.ReleaseComObject(pMapDoc);
int m = Marshal.ReleaseComObject(pMap);
axMapControl1.Refresh();
}
发现内存还是一直上涨,但是没有出现资源不足的错误。但是帮助文档的解释是对MapDocument对象进行重置。
1. 测试下面的代码:
IMapDocument pMapDoc = new MapDocumentClass();
pMapDoc.Open("D:\\演示数据\\专题[url=file://\\Untitled.mxd]\\Untitled.mxd[/url]", "");
IMap pMap = pMapDoc.get_Map(0);
pMapDoc.Close();
int n = Marshal.ReleaseComObject(pMapDoc);
int m = Marshal.ReleaseComObject(pMap);
MessageBox.Show( n.ToString() + ":" + m.ToString());
返回值为:0:0,可以看出IMap pMap = pMapDoc.get_Map(0);增加了一次对Map的引用计数。
2. 下面的代码:
IMapDocument pMapDoc = new MapDocumentClass();
pMapDoc.Open("D:\\演示数据\\地质专题\\Untitled.mxd", "");
IMap pMap = pMapDoc.get_Map(0);
pMapDoc.Close();
axMapControl1.Map = pMap;
int n = Marshal.ReleaseComObject(pMapDoc);
int m = Marshal.ReleaseComObject(pMap);
MessageBox.Show( n.ToString() + ":" + m.ToString());
返回值为0:1,可以看出 axMapControl1.Map = pMap;增加一次对Map对象的引用计数。
3.下面代码:
IMapDocument pMapDoc = new MapDocumentClass();
pMapDoc.Open("D:\\演示数据\\地质专题[url=file://\\Untitled.mxd]\\Untitled.mxd[/url]", "");
IMap pMap = pMapDoc.get_Map(0);
pMapDoc.Close();
axMapControl1.Map = pMap;
int n = Marshal.ReleaseComObject(pMapDoc);(1)
int m = Marshal.ReleaseComObject(pMap);(2)
m = Marshal.ReleaseComObject(pMap); (2)
GC.Collect();(3)
axMapControl1.Refresh();
对(1)(2)(2)(3)执行组合,(1)(2),(1)(2)(2),(1)(2)(2)(3)三种组合,发现最后一种效果很明显,内存不会持续增长,会呈现波动。但是前两种内存会持续增长。难道强制GC清理的效果这么明显?产生的内存增长不是因为COM对象、Mxd文件,而是托管的内存?希望大侠解释一下。
接下文:http://www.cnblogs.com/yhlx125/archive/2011/12/13/2286108.html