Исключение 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() требуется.