【问题标题】:Why does my COM component throw OutOfMemoryException, but runs fine on VB6?为什么我的 COM 组件抛出 OutOfMemoryException,但在 VB6 上运行良好?
【发布时间】:2010-12-07 20:08:59
【问题描述】:

我有一个用 VB6 编写的 COM 组件,我正在通过 COM 互操作从 Visual Basic .NET 应用程序使用它。

有一种方法在从 VB.NET 调用时会引发 OutOfMemoryException。但是,当我从 Visual Basic 6 应用程序运行完全相同的调用时,一切正常。

我无法在此处发布代码,因为它太长且难以遵循(我的任务是修复它)并且我无法查明问题,因为当我尝试从 VB6 调试它时,错误不会出现。

什么可能导致这种不同的行为?

在我使用 Dictionary 对象(来自 Microsoft Scripting Runtime 的 COM 对象)修复性能问题之前,这一直运行良好。除非字典不合理地增长或泄漏,否则我看不出它是如何导致这种情况的,因为它永远不会超过 100-200 个元素,并且在抛出异常之前只创建一个。

我尝试使用 Collection 对象和笨拙的 Exists() 函数进行相同的修复,但同样的问题发生了。如果我通过返回 Nothing 提前退出,它会起作用(即,它会抛出 NullReferenceException,正如预期的那样)。

【问题讨论】:

  • 有什么有意义的stacktrace吗?那么记忆呢?进程大小是多少?
  • @Simon:堆栈跟踪在 COM/.NET 边界处结束。当异常飞越堆栈时,该过程大约为 100 兆。
  • Scripting.Dictionary COM 对象被标记为 STA (Apartment),如下文 weloytty 所述,您检查过线程吗?顺便说一句,您是否使用多个线程访问这本字典?
  • 如果您在 64 位机器上运行,请尝试编译 32 位目标而不是任何 CPU 或 64 位。
  • @Simon:不,我只从一个线程访问它一次。我明天得去检查一下公寓,但我相信是 STA,因为这是一个 WinForms 应用程序,而 WinForms 是在 STA 上运行的。

标签: .net com-interop


【解决方案1】:

VB.NET 中的内存管理非常不同。 VB6 使用引用计数来释放 COM 对象,VB.NET 使用垃圾收集器。您可能会遇到一种情况,尤其是在对组件进行单元测试时,GC 运行的频率不足以释放 COM 对象。这些对象分配非托管内存,不会对垃圾收集器施加压力以启动收集。

使用性能监视器 Perfmon.exe 开始对此进行诊断。右键单击图表,添加计数器并选择 .NET CLR 内存,# Gen 0 集合并选择您的进程。验证您是否看到计数器以合理的速率递增,至少每秒一次。

解决这样的问题是相当不愉快的,请确保这不是由隔离组件引起的人为问题。或者代码中的一个普通错误,保留对 COM 对象的引用。调用 GC.Collect() 后跟 GC.WaitForPendingFinalizers() 将释放 COM 对象。调用 Marshal.ReleaseComObject() 也是如此。使用 GC.AddMemoryPressure() 也是一种可能的解决方法,但您必须合理地了解 COM 对象需要多少非托管内存。

【讨论】:

  • 由于保留对 COM 对象的引用而导致的错误不太可能发生,因为我没有触及代码的 .NET 部分。使用 perfmon,我在执行期间看到的集合很少:在抛出之前只有 10 个 gen 0 集合和 2 个在 gen 2 上。现在我通过将性能修复推送到 .NET 层来解决问题......
【解决方案2】:

你设置了 STAThread 吗? VB6 COM 组件需要 STA 线程,默认情况下,您的 VB.NET 应用程序不会运行。见http://msdn.microsoft.com/en-us/library/system.stathreadattribute(VS.71).aspx

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 1970-01-01
    • 2021-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多