.net 为什么发布的ArcObjects COM对象在GC手动强制完成之前不会最终确定?

.net 为什么发布的ArcObjects COM对象在GC手动强制完成之前不会最终确定?,.net,com,memory-leaks,garbage-collection,arcobjects,.net,Com,Memory Leaks,Garbage Collection,Arcobjects,我正在开发一个用于处理地理数据的.NET应用程序,该应用程序通过ESRI自己的.NET互操作程序集使用ESRI的ArcObjects COM库 在生产环境中运行时,由于达到每个进程2GB的内存限制,进程在某些操作期间可能会崩溃。(ArcObjects是一个32位库。)这是因为某些处理步骤可以创建许多临时ArcObjects几何体对象。它将泄漏内存,并最终耗尽内存,尽管使用和关联的辅助方法手动释放这些对象。但是,我可以通过调用强制GC释放内存,并定期与GC一起调用。Collect和FinalRel

我正在开发一个用于处理地理数据的.NET应用程序,该应用程序通过ESRI自己的.NET互操作程序集使用ESRI的ArcObjects COM库

在生产环境中运行时,由于达到每个进程2GB的内存限制,进程在某些操作期间可能会崩溃。(ArcObjects是一个32位库。)这是因为某些处理步骤可以创建许多临时ArcObjects几何体对象。它将泄漏内存,并最终耗尽内存,尽管使用和关联的辅助方法手动释放这些对象。但是,我可以通过调用强制GC释放内存,并定期与
GC一起调用。Collect
FinalReleaseComObject
控制内存使用。否则,许多对象将保留在内存中,直到进程退出(正常或异常)

第一个问题:为什么ArcObjects COM对象持有的内存没有立即释放?或者,为什么GC允许进程崩溃,而不是在崩溃之前完成释放的COM对象并回收内存

该应用程序在Windows 2008 64位上运行,而我使用Windows 7 32位进行开发。我可以让流程在生产箱上崩溃,但不能在开发箱上崩溃。我认为这可能是因为在本地,我通常在VisualStudio中运行调试版本,但我也尝试过在没有调试程序的情况下使用发布版本(启动时不进行调试),但即使如此,它也没有像在生产中那样使用内存,也不会崩溃

第二个问题:为什么


编辑:在我以前的实验中,我发现,
GC.Collect
本身是不够的,即使我显式地调用它。我有一个实用方法,它调用
GC.Collect
,然后调用
GC.WaitForPendingFinalizers
,并在每次算法迭代后调用它,以降低内存使用率。

托管应用程序中使用的COM对象位于运行时可调用包装器(RCW)后面它是一个托管对象,将COM对象的接口复制到托管客户端(它是托管组件和非托管组件之间的桥梁)。如果我没记错的话,实际的COM接口引用由RCW持有,而不是由您的代码持有。当您释放COM对象时,实际上是RCW被释放了,但是作为一个托管对象本身,它不会消失,直到GC开始清理它。当这种情况发生时,RCW被删除,对COM对象的最后一次引用消失,因此它可以销毁自己。(根据文档,
FinalReleaseComObject
应将引用计数设置为0,但我在过去见过类似的行为,因此我怀疑文档是否正确。)


至于你的第二个问题,我有一个猜测:我看到在压力大的环境中,当系统处于重载状态时,GC根本没有机会运行。当时,我们确定GC运行在一个低优先级线程上,并且我们的应用程序使用了太多CPU,以至于GC从来没有机会清理。我们不得不每隔一段时间添加另一个定期调用
GC.Collect()
的线程,这迫使GC线程觉醒并发挥其魔力。你可能会面临类似的情况。(这也可能是#1中出现your问题的原因。)

我确实知道RCW等问题,但是COM对象持有的内存没有被释放,尽管GC是显式运行的。我已经更新了这个问题。@Gnat您需要
GC.WaitForPendingFinalizers()
的原因是
GC.Collect()
实际上没有运行GC。它要求GC执行一个收集周期,但GC正在另一个线程上运行,可能无法在高负载下进行调度
GC.WaitForPendingFinalizers()
强制等待到下一个收集周期结束-这将释放CPU,GC有机会运行并完成内存清理。@Gnat如果您询问调用
GC.WaitForPendingFinalizers()是否正确(或预期),则选择“是”,如果您的应用程序在CPU上加载了负载,则会出现这种情况。在高负载、高内存的情况下,GC线程可能会耗尽,系统(或进程)可能会耗尽内存。在我提到的那个老项目中,我也做了同样的工作。我做了一些挖掘,看来你可能是对的。该进程在工作站GC模式下运行(至少在本地),并表示GC线程必须与CPU竞争,并且具有本机代码(即应用程序正在执行的大部分操作)的线程不会挂起。各种资源(例如)说,等待终结器是个坏主意,但我看不到解决方法。由于解释性评论,答案被接受