Опитвам се да отстраня грешки в производствен проблем с услуга на 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)
Така че тук можете да видите няколко нишки, които имат деактивиран превантивен GC (нишки 26,27,28,29) и една (нишка 30), която чака тези нишки, за да изпълни GC.
Моят Google-fu ме доведе до тази публикация в блог, която описва това, което звучи като подобен проблем, само че в моя случай не е включен XML. Това ми даде достатъчно информация, за да знам къде да ровя, и в крайна сметка открих, че една от общите характеристики на нишките с деактивиран превантивен GC е проследяване на стека, което изглеждаше така в горната част:
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
е причината за това блокиране? И ако е така: Какво мога да направя по въпроса?
Мога да деактивирам tailcalls на моите .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 е нишката, която стартира GC (нишка 30 в частичния списък, който включих по-горе).
И !syncblk
показва само елементи, притежавани от клиента ZooKeeper (който, макар и досаден, не участва в нито един от стековете, които пречат на стартирането на GC)
!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