Я пытаюсь отладить производственную проблему с помощью службы Windows, которая имеет тенденцию быстро выходить из строя после нескольких активных одновременных подключений. Благодаря магии дампа ядра и DebugDiag я смог обнаружить, что была отложенная операция GC, которая не могла начаться, пока несколько потоков с отключенным Preemptive GC не завершили свою работу.
Вот пример дампа потока из WinDbg, показывающий нарушающие потоки:
26 6e 1444 00..440 8009222 Disabled 00..200:00..f88 00..7a0 0 MTA (Threadpool Completion Port)
27 c1 1a0c 00..fe0 8009222 Disabled 00..e90:00..f88 00..7a0 0 MTA (Threadpool Completion Port)
28 b5 17bc 00..6f0 8009222 Disabled 00..268:00..f88 00..7a0 0 MTA (Threadpool Completion Port)
29 89 1f1c 00..ab0 8009222 Disabled 00..a30:00..f88 00..7a0 0 MTA (Threadpool Completion Port)
30 ac 2340 00..f70 8009220 Disabled 00..d00:00..d08 00..7a0 1 MTA (GC) (Threadpool Completion Port)
31 88 1b64 00..fd0 8009220 Enabled 00..b28:00..b48 00..7a0 0 MTA (Threadpool Completion Port)
Итак, здесь вы можете увидеть несколько потоков, у которых отключен вытесняющий сборщик мусора (потоки 26,27,28,29), и один (поток 30), который ожидает от этих потоков выполнения сборщика мусора.
Мой Google-fu привел меня к это сообщение в блоге, в котором описывается похожая проблема, только в моем случае XML не использовался. Это дало мне достаточно информации, чтобы знать, где копать, и в конце концов я обнаружил, что одной из общих особенностей потоков с отключенным вытесняющим сборщиком мусора была трассировка стека, которая выглядела так вверху:
ntdll!NtWaitForSingleObject+a
ntdll!RtlpWaitOnCriticalSection+e8
ntdll!RtlEnterCriticalSection+d1
ntdll!RtlpLookupDynamicFunctionEntry+58
ntdll!RtlLookupFunctionEntry+a3
clr!JIT_TailCall+db
...
DebugDiag также предупредил меня о CriticalSection, и так получилось, что потоки с JIT_TailCall
также являются единственными потоками с RtlEnterCriticalSection
Итак, мой вопрос: Действительно ли инструкция .tail
вызывает этот тупик? И если да: Что я могу с этим поделать?
Я могу отключить хвостовые вызовы в моих файлах .fsproj, но похоже, что по крайней мере один из них исходит от FSharp.Core.dll
, и некоторые спелеологические операции в декомпиляторе, похоже, подтверждают существование инструкции .tail
. Поэтому я не знаю, что изменение конфигурации проекта удалит все .tail
инструкции.
Кто-нибудь раньше сталкивался с чем-то подобным?
Обновление: дополнительная информация, которая может быть полезна.
Вот результат !locks
для этого дампа:
!locks
CritSec +401680 at 0000000000401680
WaiterWoken No
LockCount 0
RecursionCount 1
OwningThread 2340
EntryCount 0
ContentionCount bf
*** Locked
Scanned 1657 critical sections
Поток 2340 - это поток, который запустил сборщик мусора (поток 30 в частичном списке, который я включил выше).
И !syncblk
показывает только элементы, принадлежащие клиенту ZooKeeper (который, хотя и раздражает, не участвует ни в одном из стеков, которые не позволяют запускать сборщик мусора).
!syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
11 0000000019721a38 1 1 0000000019766e20 638 7 0000000000fb2950 System.Collections.Generic.LinkedList`1[[ZooKeeperNet.Packet, ZooKeeperNet]]
Waiting threads:
18 0000000019721c68 1 1 000000001ae71420 8ac 13 00000000012defc8 System.Collections.Generic.LinkedList`1[[ZooKeeperNet.Packet, ZooKeeperNet]]
Waiting threads:
-----------------------------
Total 64
CCW 0
RCW 0
ComClassFactory 0
Free 5