CLR .tail指令是否禁用抢占式GC?

时间:2013-09-06 20:29:04

标签: .net f# garbage-collection clr tail-recursion

我尝试使用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引导我this blog post,它描述了类似问题,只是在我的情况下,没有涉及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指令的存在。所以我不知道chaning项目配置会删除所有.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

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

可能有点晚了,虽然你所描述的问题看起来与我的问题有点不同,但你给出的呼叫追踪表明可能存在一些共同点。

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

此外,由于您遇到了垃圾收集问题,因此您可能需要查看使用server garbage collection。这是我在找到上述问题之前所做的一件事,它解决了我们遇到的很大一部分性能问题。所需的只是app.config中的以下内容:

<configuration>
  ...
  <runtime>
    ...
    <gcServer enabled="true"/>
    ...
  </runtime>
  ...
</configuration>