Sto tentando di eseguire il debug di un problema di produzione con un servizio di Windows che tende a ridursi rapidamente dopo l'attivazione di un numero di connessioni simultanee. Attraverso la magia di un core dump e DebugDiag sono stato in grado di scoprire che c'era un'operazione GC in sospeso, che non poteva essere avviata fino a quando diversi thread con Preemptive GC disabilitato completavano il loro lavoro.L'istruzione CLR .tail disabilita il GC preemptive?
Ecco una discarica filo campione WinDbg mostrando i fili incriminate:
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)
Così qui si possono vedere diverse discussioni che hanno GC preventiva disabilitato (fili 26,27,28,29) e uno (filo 30) che è in attesa su quei thread per eseguire il GC.
Il mio Google-fu mi porta a this blog post che descrive quello che sembra un problema simile, solo nel mio caso non c'era coinvolto XML. Mi ha dato abbastanza informazioni per sapere dove scavare, però, e alla fine ho scoperto che una delle caratteristiche comuni dei filetti con GC preventiva disabilitati era una traccia dello stack che si presentava così in alto:
ntdll!NtWaitForSingleObject+a
ntdll!RtlpWaitOnCriticalSection+e8
ntdll!RtlEnterCriticalSection+d1
ntdll!RtlpLookupDynamicFunctionEntry+58
ntdll!RtlLookupFunctionEntry+a3
clr!JIT_TailCall+db
...
DebugDiag anche mi ha avvertito circa il CriticalSection, e si dà il caso che i fili con il JIT_TailCall
sono gli anche l'unico thread con RtlEnterCriticalSection
quindi la mia domanda è: e ', infatti, l'istruzione .tail
che sta causando questa impasse? E in caso affermativo: Cosa posso fare a riguardo?
posso disabilitare tailcalls sul mio file .fsproj ma sembra che almeno uno di questi proviene da FSharp.Core.dll
e alcuni speleologia nel decompilatore sembra confermare l'esistenza dell'istruzione .tail
. Quindi non so che modificare la configurazione del progetto rimuoverà tutte le istruzioni .tail
.
Qualcuno ha mai trattato qualcosa del genere?
Aggiornamento: Altre informazioni che potrebbero essere utili.
Ecco l'output di !locks
per questa discarica:
!locks
CritSec +401680 at 0000000000401680
WaiterWoken No
LockCount 0
RecursionCount 1
OwningThread 2340
EntryCount 0
ContentionCount bf
*** Locked
Scanned 1657 critical sections
Discussione 2340 è il thread che ha iniziato il GC (filo 30 nella lista parziale ho incluso sopra).
E !syncblk
sta mostrando solo gli elementi di proprietà del cliente ZooKeeper (che, pur fastidioso, non è coinvolto in nessuna delle pile, che stanno mantenendo GC dalla partenza)
!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
Ho un paio di posizioni che utilizzano i blocchi, quindi le classi standard .Net Monitor, ma le tracce dello stack in cui vengono visualizzate non sono dove vicino a quel codice. Questo è fondamentalmente un processo di lista (quindi List.iter, Map.find, ecc.). La cosa interessante è che tutti i thread eseguono più o meno la stessa azione, ma dei 60 circa che sono connessioni attive, solo 6 hanno GC preemptive disabilitato – ckramer
E 'possibile che 'List.iter (fun _ ->. . lock ..) xs' farebbe in modo che le tracce dello stack facciano riferimento a 'List.iter'? – t0yv0
Non ho alcun blocco nelle tracce che mostrano che GC preventiva è disabilitato. Si verificano anche in diverse funzioni ottimizzate per la coda (quindi in un caso è MapTreeInternal.mapi, un'altra è Primitives.Basics.List.iter, un'altra ancora è in MapTreeModule.find). Queste chiamate funzionano anche sui tipi di record F #, per quel che posso dire non ci sono nemmeno istanze usa e getta qui, per non parlare delle risorse non gestite. Una cosa che tutti i thread hanno in comune è che vengono richiamati tramite un'operazione di ricezione TCP asincrona. Non so se questo sia in qualche modo coinvolto o no. – ckramer