2

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

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

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

该应用程序在 Windows 2008 64 位上运行,而我使用 Windows 7 32 位进行开发。我可以让进程在生产盒上崩溃,但不能在我的开发盒上崩溃。我认为这可能是因为我通常在本地使用 Debug 版本在 Visual Studio 中运行,但我也使用 Release 版本在没有调试器的情况下尝试过(不使用调试启动),但即便如此,它也没有使用任何地方。内存在生产中,不会崩溃。

第二个问题:为什么?

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

4

1 回答 1

5

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

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

于 2012-10-08T01:21:10.250 回答