.NET 应用程序因 GC 线程死锁而挂起

标签 .net multithreading garbage-collection deadlock critical-section

我们的应用程序混合使用托管 (C#) 和非托管 (C++) 代码时遇到问题。基本上,我们有一个调用一堆程序集的 exe,其中一个程序集是我们 C++ 库的 MC++ 包装器。该应用程序是一个控制台应用程序。大多数时候它工作正常,但偶尔它会挂起而没有任何错误或异常。

使用内存转储和符号,我们已经能够在 WinDbg 中进行一些诊断,但我不确定我们所看到的是否是死锁。我搜索了堆栈中出现的 CLR 方法名称,但无法找到一个线程试图分配内存并被 GC 死锁的情况。

到目前为止,我已经尝试过带有 sos、sosex、psscor4 扩展的 WinDbg。有趣的是 sosex 有一个检查死锁的命令(!dlk),但它报告没有死锁。

很难发布代码,因为它是一个庞大而复杂的应用程序。 .NET 3.5 和 4.0 程序集混合在一起。托管代码和非托管代码中都有线程。

如果有人可以查看堆栈跟踪并确认这可能是 GC 线程的死锁,我会很高兴。如果您可以建议一些其他方法来调试使用 C# 和 MC++ 的 .NET 应用程序中的死锁/挂起,甚至更好。

这是我到目前为止所拥有的:

应用挂起时的线程列表:(!threads)

ThreadCount:      8
UnstartedThread:  0
BackgroundThread: 5
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                           PreEmptive                                                   Lock
       ID  OSID        ThreadOBJ     State GC       GC Alloc Context                  Domain           Count APT Exception
   0    1   de0 00000000008069f0      a020 Enabled  0000000000000000:0000000000000000 00000000007fa280     0 MTA
   2    2  2130 000000000080bd30      b220 Enabled  0000000000000000:0000000000000000 00000000007fa280     0 MTA (Finalizer)
   4    3  14fc 000000001d182880   200b020 Enabled  0000000000000000:0000000000000000 00000000007fa280     0 MTA
   5    4  20d0 000000001d18b400      b220 Enabled  0000000000000000:0000000000000000 00000000007fa280     2 MTA (GC)
   6    5  18a8 000000001d19f6a0      b020 Enabled  0000000000000000:0000000000000000 00000000007fa280     0 MTA
   7    6  18a0 000000001d1c6f10       220 Enabled  0000000000000000:0000000000000000 00000000007fa280     0 Ukn
   8    7  12f4 000000001d1c1ee0       220 Enabled  0000000000000000:0000000000000000 00000000007fa280     0 Ukn
  10    8  2170 000000001d1c2ad0       220 Enabled  0000000000000000:0000000000000000 00000000007fa280     0 Ukn

       OSID     Special thread type
    1   2570    DbgHelper 
    2   2130    Finalizer 
    5   20d0    SuspendEE 
   12   1890    GC

这是 GC 线程的堆栈的样子:
OS Thread Id: 0x1890 (12)
Child-SP         RetAddr          Call Site
0000000023e9f898 000000007799e4e8 ntdll!ZwWaitForSingleObject+0xa
0000000023e9f8a0 000000007799e3db ntdll!RtlpWaitOnCriticalSection+0xe8
0000000023e9f950 000007fef95d603e ntdll!RtlEnterCriticalSection+0xd1
0000000023e9f980 000007fef947bc41 clr!UnsafeEEEnterCriticalSection+0x1f
0000000023e9f9b0 000007fef947613a clr!CrstBase::Enter+0x1a1
0000000023e9f9f0 000007fef95da3a2 clr!ThreadStore::LockThreadStore+0x9a
0000000023e9fa20 000007fef9679675 clr!WKS::GCHeap::SuspendEE+0x82
0000000023e9fb20 000007fef9677eb2 clr!WKS::gc_heap::bgc_suspend_EE+0x25
0000000023e9fb50 000007fef98455b0 clr!WKS::gc_heap::background_mark_phase+0x236
0000000023e9fbb0 000007fef9677b76 clr! ?? ::FNODOBFM::`string'+0x9f85d
0000000023e9fc00 00000000773d652d clr!WKS::gc_heap::gc_thread_function+0xd3
0000000023e9fc30 000000007797c521 KERNEL32!BaseThreadInitThunk+0xd
0000000023e9fc60 0000000000000000 ntdll!RtlUserThreadStart+0x1d

在我看来,GC 线程正在等待临界区。我们能够找到关键部分地址,然后找到它的所有者线程 (!critsec)。所有者线程的堆栈如下所示。我已经对其进行了修剪以保持这篇文章的简短。 (!dumpstack)
OS Thread Id: 0x20d0 (5)
Child-SP         RetAddr          Call Site
000000001fc5dd38 000007fefe0510dc ntdll!ZwWaitForSingleObject+0xa
000000001fc5dd40 000007fef9478817 KERNELBASE!WaitForSingleObjectEx+0x79
000000001fc5dde0 000007fef94787c0 clr!CLREvent::WaitEx+0x170
000000001fc5de20 000007fef947866b clr!CLREvent::WaitEx+0xf8
000000001fc5de80 000007fef967a15b clr!CLREvent::WaitEx+0x5e
000000001fc5df20 000007fef967a001 clr!WKS::gc_heap::user_thread_wait+0x49
000000001fc5df50 000007fef95dbb4e clr! ?? ::FNODOBFM::`string'+0x9fcc4
000000001fc5e030 000007fef95da22e clr!WKS::GCHeap::GarbageCollectGeneration+0x14e
000000001fc5e080 000007fef95d9e4e clr!WKS::gc_heap::try_allocate_more_space+0x25f
000000001fc5e150 000007fef95d9fc8 clr!WKS::GCHeap::Alloc+0x7e
000000001fc5e180 000007fef947407c clr!AllocateArrayEx+0xa6b
000000001fc5e2f0 000007fef8555b75 clr!JIT_NewArr1+0x45c
000000001fc5e4c0 000007fef8561103 mscorlib_ni!System.Reflection.CustomAttributeData.GetCustomAttributeRecords(System.Reflection.RuntimeModule, Int32)+0x115
000000001fc5e590 000007fef855db55 mscorlib_ni!System.Reflection.CustomAttribute.IsCustomAttributeDefined(System.Reflection.RuntimeModule, Int32, System.RuntimeType, Boolean)+0x103
000000001fc5e720 000007fef856c8ac mscorlib_ni!System.Reflection.CustomAttribute.IsDefined(System.RuntimeType, System.RuntimeType, Boolean)+0x75
000000001fc5e770 000007fef857fe46 mscorlib_ni!System.Enum.InternalFormat(System.RuntimeType, System.Object)+0x2c
000000001fc5e7b0 000007fef8554f3b mscorlib_ni!System.Text.StringBuilder.AppendFormat(System.IFormatProvider, System.String, System.Object[])+0x2e6
000000001fc5e850 000007ff03c640fc mscorlib_ni!System.String.Format(System.IFormatProvider, System.String, System.Object[])+0x7b
000000001fc5e8b0 000007ff03c638a6 MyLibrary1!NumberCache.NumberEntry.ToString()+0x26c

最佳答案

第二个调用堆栈中的这一行看起来很可疑:

000000001fc5df50 000007fef95dbb4e clr! ?? ::FNODOBFM::`string'+0x9fcc4 

看看偏移地址有多大,我没有看到任何模块名称——你是不是漏掉了一些符号?

也许该库中有一个导致问题的终结器。

关于.NET 应用程序因 GC 线程死锁而挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9289643/

相关文章:

Java:clear()大尺寸列表有助于快速垃圾收集吗?

.net - C# 如果我以优先级 = RealTime 运行进程,GC 会发生什么?

c# - .Net 中的位图保存是否以不正确的格式保存图像?

c# - 来自多线程应用程序的数据库表插入锁

c# - Windows Phone 7.1 + Azure WebRole 中的错误处理/报告

c - 在 c99 中使用 __thread

ios - 核心数据的UISearchBar性能问题

python - 为低系统资源的嵌入式系统优化 Python 运行时指南

.net - 如何在 WPF 应用程序中生成 "print preview"的 FlowDocument?

c# - 适用于 Windows 10 的 AppFabric 支持