有很多关于释放COM对象和垃圾收集的问题,但我没有找到具体解决这个问题的任何问题。
当释放COM对象(在本例中特别是Excel Interop)时,我应该以什么顺序释放引用并调用垃圾收集?
在某些地方(例如here)我看到了这个:
Marshall.FinalReleaseComObject(obj);
GC.Collect();
GC.WaitForPendingFinalizers();
而在其他人(例如here)中:
GC.Collect();
GC.WaitForPendingFinalizers();
Marshall.FinalReleaseComObject(obj);
或者不重要,我什么都不担心?
答案 0 :(得分:4)
Marshal.FinalReleaseComObject()释放底层的COM接口指针。
GC.Collect()和GC.WaitForPendingFinalizers()导致调用COM包装器的终结器,调用FinalReleaseComObject()。
所以没有意义的是两种方式。选择其中一个。
显式调用FinalReleaseComObject()的麻烦在于它只能在为所有接口指针调用它时才能工作。如果您错过其中一个,Office程序将继续运行。那个非常很容易做到,特别是C#版本4中允许的语法糖使其成为可能。像range = sheet.Cells[1, 1]
这样的表达式,在Excel互操作代码中很常见。那里有一个隐藏的Range接口引用,你永远不会明确地存储在任何地方。所以你也不能发布它。
这不是GC.Collect()的问题,它可以看到它们。然而,它并非完全没有问题,它只会在你的程序不再引用接口时收集并运行终结器。这肯定是你的第二个片段的错误。调试程序时,调试器往往会出错,调试器会将本地对象引用的生命周期延长到方法的末尾。还有你看看Taskmgr并大喊“死该死!”的时间。
GC.Collect()的常用建议也适用于此处。让您的程序保持运行并执行工作。通常情况下,您将触发垃圾收集并释放COM包装器。 Office程序将退出。它不会立即发生,最终会发生。
答案 1 :(得分:1)
COM使用的引用计数机制是另一种自动内存管理方式,但对内存和行为的影响略有不同。
任何引用计数实现都为资源清理提供了确定性行为。这意味着在调用Marshal.FinalReleaseComObject()
之后,将回收与COM对象相关的所有资源(内存和其他资源)。
这意味着如果我们有其他托管对象,并且您希望尽快回收它们,则应首先释放COM对象,并且仅在调用GC.Collect
方法之后。