释放 Com 对象不影响内存使用

Releasing Com Objects Doesnt Affect Memory Usage

当我通过Marshall.ReleaseComObject方法释放com对象时,应用程序的内存使用没有改变。不使用 Marshall.ReleaseComObject,而是使用垃圾收集器 (GC.Collect()) 可以释放 com 对象的内存区域,但是 UI 变慢了。

所以我的问题是,释放 com 对象的最佳方法是什么?

试试这个,

System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oApp);
oApp = null; // Set each COM Object to null
//
// After all of the COM objects have been released and set to null, do the following:
GC.Collect(); // Start .NET CLR Garbage Collection
GC.WaitForPendingFinalizers(); // Wait for Garbage Collection to finish

如果对象实现终结器,调用gc意味着将这个对象引用放入终结队列,意味着它不会立即释放。

虽然有点过时(从 ArcGIS 10.0 开始),但在 http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#/Releasing_COM_references/0001000004tm000000/ 上对发布规则和如何发布规则进行了很好的描述。

有两种方法可以做到这一点。通过使用 Marshal.ReleaseCOMObjectComReleaser-class 本质上是前者的包装。然而,您可能有多个对完全相同的 com 对象的引用,这就是为什么调用 ReleaseComObject 不会 最终 释放对象,而只是将内部引用计数器减一.只有当该计数器等于零时,对象才会 实际上 被释放。例如,请参见此处:

var f1 = featureClass.GetFeature(1);
// retrieve the exact same feature again
var f2 = featureClass.GetFeature(1);

尽管从 .NET 的角度来看 f1f2 是完全不同的对象,但底层的 com 对象是相同的(假设唯一实例化,这超出了本文的范围题)。在 f1f2 上调用 Marshal.ReleaseComObject 时,您只会将此 com 对象的内部引用计数器减一,留下一个引用。

GC.Collect 但是没有效果,因为它无法处理 unmanaged com 对象的资源。垃圾收集器只能释放 managed 资源。因此,调用 GC.Collect 只会——如果有的话——释放运行时可调用包装器,它是非托管对象的托管包装器。然而,后者仍然存在于内存中,并且可能会产生死泄漏。

话虽如此,最终 释放 com 对象的唯一方法是在循环中调用 Marshal.ReleaseComObject,直到引用计数器为零。

while(Marshal.ReleaseComObject(myObject) > 0);

之后你可能会也可能不会打电话 GC.Collect。但是我不建议这样做,因为垃圾收集器最清楚何时释放托管对象。强制它这样做最多只能按预期工作,最坏的情况下只会使您的代码变慢而不会产生任何积极影响。 GC 是一个不确定的过程,你不能真的影响它。

最好的方法是使用 ComReleaser 或 Marshal.ReleaseComObject 方法。过多调用 GC.Collect 方法会导致应用程序变慢。让 GC 在需要的时候完成它的工作。