8

我正在尝试使用 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是导致这种死锁的指令吗?如果是这样:我能做些什么呢?

我可以在我的 .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
4

2 回答 2

1

我怀疑尾调用是问题(否则,我怀疑更多的 F# 用户会遇到这个问题)。从调用堆栈中,您的代码似乎正在等待关键部分,这似乎更有可能是问题的根源......知道您的代码可能依赖于哪些同步原语吗?

于 2013-09-06T20:56:32.303 回答
1

可能有点晚了,尽管您描述的问题似乎与我遇到的问题有些不同,但您提供的呼叫跟踪表明可能存在一些共同点。

您可以在我对我自己的问题的回答中找到更多详细信息,但简而言之,它归结为 Windows 7 和 .NET 4.0-4.5 的组合,这使得 F# 中的尾递归出现问题,导致过度锁定。将 .NET 更新到 4.6 或升级到 Windows 8 可以解决问题。

此外,由于您在垃圾收集方面遇到问题,您可能需要考虑使用服务器垃圾收集。这是我在发现上述问题之前所做的事情之一,它解决了我们遇到的大部分性能问题。所需要的只是您的 app.config 中的以下内容:

<configuration>
  ...
  <runtime>
    ...
    <gcServer enabled="true"/>
    ...
  </runtime>
  ...
</configuration>
于 2016-07-15T09:34:41.007 回答