Исключение OutOfMemory в приложении визуализации видео WPF с COM-взаимодействием

У нас есть многофункциональное клиентское приложение, разработанное с использованием WPF/C#.Net 4.0, которое взаимодействует с собственными COM-библиотеками COM. Регулярные события вызываются через этот COM-интерфейс, содержащий видеоданные.

В рамках приложения мы визуализируем видео через Windows Media Foundation и создали взаимодействия для использования Window Media Foundation. У нас есть несколько конвейеров WMF, одновременно воспроизводящих разные видео.

Приложение работает за 6-8 часов рендеринга видео. Частные байты оставались стабильно стабильными в течение этого времени (скажем, около 500-600 МБ).

В какой-то момент приложение, кажется, зависает, в этот момент частные байты увеличиваются очень быстро, пока процесс не потребляет приблизительно 1,4 ГБ памяти и не завершается с OutOfMemoryException.

Мы воспроизвели это на 5 разных рабочих станциях с разными графическими картами (карты NVIDIA и ATI) и смесью Windows 7 32 и 64 бит.

Мы проанализировали 3 файла дампа и обнаружили, что поток финализатора ожидает вызова метода ole32.GetToSTA(). Мы не можем определить, что вызывает блокировку потока финализатора и как решить эту проблему. Я вставил выдержки из трех дампов, которые мы анализировали:

Дамп 1)

Поток 2:ae0 ожидает в потоке STA efc

Поток 28:efc вызывает WaitForSingleObject. Дескриптор, на котором он находится, фактически является дескриптором потока 5ab4, который является идентификатором потока 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

dump2)

Нить 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

Dump3)

Поток финализатора находится в вызове GetToSTA, поэтому он ожидает освобождения COM-объекта

Поток 29 является COM-объектом в STA, и он ожидает в критической секции, принадлежащей потоку 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

Любые идеи о том, как мы могли бы решить эту проблему?

1 ответ

Ну, поток финализатора заблокирован. Это, безусловно, приведет к возможному OOM. Мы не можем увидеть полную трассировку стека для потока финализатора, но есть некоторые шансы, что вы увидите SwitchAptAndDispatchCall() и ReleaseRCWListInCorrectCtx() в трассировке, что указывает на то, что он пытается вызвать IUnknown::Release() для освобождения COM-объекта, И этот объект является многопоточным, поэтому для безопасного вызова требуется переключатель потока.

Я не вижу достойных кандидатов в трассировке стека, которую вы разместили, возможно, потому что вы не получили правильного, или поток уже занят закрытием из-за исключения. Попытайтесь поймать это раньше с перерывом в отладчике, как только вы увидите увеличение виртуальной памяти.

Наиболее частая причина подобной тупиковой ситуации - нарушение требований к потоку STA. В каком состоянии он никогда не должен блокироваться и должен прокачивать цикл сообщений. Требование никогда не блокировать, как правило, легко выполняется в программе.NET, при необходимости CLR запускает цикл обработки сообщений, когда вы используете оператор блокировки или вызов WaitHandle.WaitXxx(). Однако очень часто забывают прокачать цикл сообщений, тем более что это довольно болезненно. Application.Run() требуется.

Другие вопросы по тегам