c# - 具有 COM 互操作性的 WPF 视频渲染应用程序中的 OutOfMemory 异常

标签 c# wpf com-interop sta

我们有一个使用 WPF/C#.Net 4.0 开发的富客户端应用程序,它与内部 COM DLL 进行互操作。定期事件通过包含视频数据的 COM 接口(interface)引发。

作为应用程序的一部分,我们通过 Windows Media Foundation 渲染视频,并创建了互操作来使用 Window Media Foundation。我们有多个 WMF 管道同时渲染不同的视频。

应用程序运行 6-8 小时渲染视频。在此期间,专用字节保持稳定(例如 500-600MB 左右)。

在某个时刻,应用程序似乎挂起,此时私有(private)字节会非常迅速地增加,直到进程消耗大约 1.4GB 内存并因 OutOfMemoryException 崩溃。

我们在 5 台具有不同显卡(NVIDIA 和 ATI 卡)的不同工作站以及 Windows 7 32 和 64 位混合系统上重现了此内容。

我们分析了 3 个转储文件,发现终结器线程正在等待对 ole32.GetToSTA() 方法的调用。我们无法确定导致终结器线程阻塞的原因以及如何解决此问题。我粘贴了我们分析的三个转储的摘录:

转储1)

线程 2:ae0 正在等待 STA 线程 efc

线程 28:efc 正在调用 WaitForSingleObject。它等待的句柄实际上是一个线程句柄 5ab4,线程 id 为 14a4

线程 130:14a4 具有以下堆栈:

37f4fdf4 753776a6 ntdll!NtRemoveIoCompletion+0x15
37f4fe20 63301743 KERNELBASE!GetQueuedCompletionStatus+0x29
37f4fe74 6330d0db WMNetMgr!CNSIoCompletionPortNT::WaitAndServeCompletionsLoop+0x5e
37f4fe94 633199bf WMNetMgr!CNSIoCompletionPortNT::WaitAndServeCompletions+0x4c
37f4fecc 63312dbd WMNetMgr!CWorkThreadManager::CWorkerThread::ThreadMain+0xa2
37f4fed8 769b3677 WMNetMgr!CWMThread::ThreadFunc+0x3b
37f4fee4 77679f42 kernel32!BaseThreadInitThunk+0xe
37f4ff24 77679f15 ntdll!__RtlUserThreadStart+0x70
37f4ff3c 00000000 ntdll!_RtlUserThreadStart+0x1b

转储2)

STA 线程:

1127f474 75f80a91 ntdll!ZwWaitForSingleObject+0x15
1127f4e0 77411184 KERNELBASE!WaitForSingleObjectEx+0x98
1127f4f8 77411138 kernel32!WaitForSingleObjectExImplementation+0x75
1127f50c 63ae5f29 kernel32!WaitForSingleObject+0x12
1127f530 63a8eb2e WMNetMgr!CWMThread::Wait+0x78
1127f54c 63a8f128 WMNetMgr!CWorkThreadManager::CThreadPool::Shutdown+0x70
1127f568 63a76e10 WMNetMgr!CWorkThreadManager::Shutdown+0x34
1127f59c 63a76f2d WMNetMgr!CNSClientNetManagerHelper::Shutdown+0xdd
1127f5a4 63cd228e WMNetMgr!CNSClientNetManager::Shutdown+0x66
WARNING: Stack unwind information not available. Following frames may be wrong.
1127f5bc 63cd23a6 WMVCORE!WMCreateProfileManager+0xeef6
1127f5dc 63c573ca WMVCORE!WMCreateProfileManager+0xf00e
1127f5e8 63c62f18 WMVCORE!WMIsAvailableOffline+0x2ba3b
1127f618 63c19da6 WMVCORE!WMIsAvailableOffline+0x37589
1127f630 63c1aca2 WMVCORE!WMIsContentProtected+0x56e4
1127f63c 63c14bd7 WMVCORE!WMIsContentProtected+0x65e0
1127f650 113de6e8 WMVCORE!WMIsContentProtected+0x515
1127f660 113de513 wmp!CWMDRMReaderStub::CExternalStub::ShutdownInternalRefs+0x1d0
1127f674 113c1988 wmp!CWMDRMReaderStub::ExternalRelease+0x4f
1127f67c 1160a5b9 wmp!CWMDRMReaderStub::CExternalStub::Release+0x13
1127f6a4 1161745f wmp!CWMGraph::CleanupUpStream_selfprotected+0xbe

终结器线程正在尝试切换到 STA:

0126eccc 75f80a91 ntdll!ZwWaitForSingleObject+0x15
0126ed38 77411184 KERNELBASE!WaitForSingleObjectEx+0x98
0126ed50 77411138 kernel32!WaitForSingleObjectExImplementation+0x75
0126ed64 75d78907 kernel32!WaitForSingleObject+0x12
0126ed88 75e9a819 ole32!GetToSTA+0xad

转储3)

终结器线程位于 GetToSTA 调用中,因此它正在等待 COM 对象释放

线程 29 是 STA 中的 COM 对象,它正在等待线程 53 (1bf4) 拥有的临界区

线程 53 正在做:

1cbcf990 76310a91 ntdll!ZwWaitForSingleObject+0x15
1cbcf9fc 74cb1184 KERNELBASE!WaitForSingleObjectEx+0x98
1cbcfa14 74cb1138 kernel32!WaitForSingleObjectExImplementation+0x75
1cbcfa28 65dfb6bb kernel32!WaitForSingleObject+0x12
WARNING: Stack unwind information not available. Following frames may be wrong.
1cbcfa48 74cb3677 wmp!Ordinal3000+0x53280
1cbcfa54 77029f42 kernel32!BaseThreadInitThunk+0xe
1cbcfa94 77029f15 ntdll!__RtlUserThreadStart+0x701cbcfaac 00000000 ntdll!_RtlUserThreadStart+0x1b

关于我们如何解决这个问题有什么想法吗?

最佳答案

嗯,终结器线程陷入了死锁。这肯定会导致最终的 OOM。我们看不到终结器线程的完整堆栈跟踪,但您可能会在跟踪中看到 SwitchAptAndDispatchCall() 和 ReleaseRCWListInCorrectCtx(),这表明它正在尝试调用 IUnknown::Release() 来释放 COM 对象。该对象是单元线程的,因此需要线程切换才能安全地进行调用。

我在您发布的堆栈跟踪中没有看到任何合适的候选者,可能是因为您没有找到正确的候选者,或者线程已经因异常而忙于关闭。一旦看到虚拟内存大小攀升,请尝试通过调试器中断尽早捕获它。

造成此类死锁的最常见原因是违反了 STA 线程的要求。其中声明它决不能阻塞并且必须泵送消息循环。在 .NET 程序中,永不阻塞的要求通常很容易满足,当您使用 lock 语句或 WaitHandle.WaitXxx() 调用时,CLR 将在必要时泵送消息循环。然而,忘记泵送消息循环是很常见的,特别是因为这样做有点痛苦。需要Application.Run()。

关于c# - 具有 COM 互操作性的 WPF 视频渲染应用程序中的 OutOfMemory 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12248678/

相关文章:

c# - 在集合中找不到参数 'daterange'

c# - 将窗口的 SetParent() 设置为 SHELLDLL_DefView 是否安全?

c# - ScrollViewer 上的 DataGrids 阻止它滚动

c# - 绑定(bind)到位于 VisualTree 中远离 MenuItem 的 ElementName

wpf - 如何 'get at' WPF 组合框 PART_EditableTextbox 因为组合框没有突出显示?

c# - 如何在 .Net 中序列化 COM 对象?

c# - w3wp.exe 中出现未处理的 win32 异常

c# - Serilog + Azure - Log Analytics 工作区中没有出现自定义日志

c# - 从开放的 HTTP 流中读取数据

c# - 基于 MySQL 表创建 C# 类