6

SO上有很多关于释放COM对象和垃圾收集的问题,但我找不到专门解决这个问题的问题。

释放 COM 对象(在这种情况下特别是 Excel 互操作)时,我应该按什么顺序释放引用并调用垃圾回收?

在某些地方(例如这里)我看到了这个:

Marshall.FinalReleaseComObject(obj);
GC.Collect();
GC.WaitForPendingFinalizers();

在其他(例如这里)中:

GC.Collect();
GC.WaitForPendingFinalizers();
Marshall.FinalReleaseComObject(obj);

还是没关系,我什么都不担心?

4

2 回答 2

4

Marshal.FinalReleaseComObject() releases the underlying COM interface pointer.

GC.Collect() and GC.WaitForPendingFinalizers() causes the finalizer for a COM wrapper to be called, which calls FinalReleaseComObject().

So what makes no sense is to do it both ways. Pick one or the other.

The trouble with explicitly calling FinalReleaseComObject() is that it will only work when you call it for all the interface pointers. The Office program will keep running if you miss just one of them. That's very easy to do, especially the syntax sugar allowed in C# version 4 makes it likely. An expression like range = sheet.Cells[1, 1], very common in Excel interop code. There's a hidden Range interface reference there that you never explicitly store anywhere. So you can't release it either.

That's not a problem with GC.Collect(), it can see them. It is however not entirely without trouble either, it will only collect and run the finalizer when your program has no reference to the interface anymore. Which is definitely what's wrong with your second snippet. And which tends to go wrong when you debug your program, the debugger extends the lifetime of local object references to the end of the method. Also the time you look at Taskmgr and yell "die dammit!"

The usual advice for GC.Collect() applies here as well. Keep your program running and perform work. The normal thing happens, you'll trigger a garbage collection and that releases the COM wrappers as well. And the Office program will exit. It just doesn't happen instantly, it will happen eventually.

于 2012-11-26T20:24:35.803 回答
1

COM 使用的引用计数机制是另一种自动内存管理方式,但对内存和行为的影响略有不同。

任何引用计数实现都为资源清理提供确定性行为。这意味着在调用Marshal.FinalReleaseComObject()与 COM 对象相关的所有资源(内存和其他资源)之后,将立即回收。

这意味着如果我们有额外的托管对象并且您想尽快回收它们,您应该首先释放 COM 对象,并且仅在该调用GC.Collect方法之后。

于 2012-11-26T19:58:38.970 回答