终结器线程 ID

5

我们的一个WCF应用程序存在内存泄漏问题,我想知道是否有人能为我澄清一些事情。使用windbg运行!finalizequeue命令,它显示每个堆中有数千个对象处于“准备完成”状态。

Heap 0
generation 0 has 464 finalizable objects (0000000033877190->0000000033878010)
generation 1 has 52 finalizable objects (0000000033876ff0->0000000033877190)
generation 2 has 19958 finalizable objects (0000000033850040->0000000033876ff0)
Ready for finalization 228791 objects (0000000033878010->0000000033a36dc8)
------------------------------
Heap 1
generation 0 has 1508 finalizable objects (000000002ee2e168->000000002ee31088)
generation 1 has 91 finalizable objects (000000002ee2de90->000000002ee2e168)
generation 2 has 23498 finalizable objects (000000002ee00040->000000002ee2de90)
Ready for finalization 249421 objects (000000002ee31088->000000002f0182f0)
------------------------------
Heap 2
generation 0 has 66 finalizable objects (00000000292660d0->00000000292662e0)
generation 1 has 63 finalizable objects (0000000029265ed8->00000000292660d0)
generation 2 has 19411 finalizable objects (0000000029240040->0000000029265ed8)
Ready for finalization 238531 objects (00000000292662e0->00000000294380f8)
------------------------------
Heap 3
generation 0 has 510 finalizable objects (0000000034e470d8->0000000034e480c8)
generation 1 has 77 finalizable objects (0000000034e46e70->0000000034e470d8)
generation 2 has 19910 finalizable objects (0000000034e20040->0000000034e46e70)
Ready for finalization 226933 objects (0000000034e480c8->0000000035003470)
Statistics for all finalizable objects (including all objects ready for finalization):

这告诉我终结器线程被卡住了。所以我在windbg中运行了!Threads命令以获取终结器线程的ID,以下是显示结果....

ThreadCount:      2969
UnstartedThread:  0
BackgroundThread: 187
PendingThread:    0
DeadThread:       2782
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
XXXX    2 19e8 0000000001f64b10 80039220 Preemptive  0000000000000000:0000000000000000 000000000d4aacb0 0     Ukn (Finalizer) 
  18    3  cb4 000000000d9bf7a0  102a220 Preemptive  0000000000000000:0000000000000000 000000000150e940 0     MTA (Threadpool Worker) 
  19    4 1a24 000000000f762720    21220 Preemptive  0000000000000000:0000000000000000 000000000150e940 0     Ukn 
  20    6  e1c 0000000010f4eae0  3029220 Preemptive  0000000000000000:0000000000000000 000000000d4aacb0 0     MTA (Threadpool Worker) 
  22   48 1548 000000001feb1880    21220 Preemptive  0000000000000000:0000000000000000 000000000150e940 0     Ukn 
  23   49 11a4 000000001feb2050    21220 Preemptive  0000000000000000:0000000000000000 000000000150e940 0     Ukn 
  24   50  a64 000000001feb2820    21220 Preemptive  0000000000000000:0000000000000000 000000000150e940 0     Ukn 

列表中的第一个线程是终结器线程,其线程ID为XXXX。这是什么意思?我猜这意味着该线程不再存在,或者没有在运行。有人可以确认或更正我的理解吗? 更新: 我像Kjell Gunnar在评论中建议的那样运行了~2s;kb命令,并输出了以下结果。
0:028> ~2s;kb
ntdll!ZwRemoveIoCompletion+0xa:
00000000`77c1bdca c3              ret
RetAddr           : Args to Child                                                           : Call Site
000007fe`fe0e16ad : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!ZwRemoveIoCompletion+0xa
00000000`776f9991 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!GetQueuedCompletionStatus+0x39
000007fe`fb7f6bb1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!GetQueuedCompletionStatusStub+0x11
00000000`777059cd : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nativerd!NOTIFICATION_THREAD::ThreadProc+0x71
00000000`77bfa561 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d 

ZwRemoveIoCompletion是什么意思?

在终结器线程中发生死锁是一种相当常见的错误。您需要查看其堆栈跟踪。 - Hans Passant
我本来打算这样做,但是难道不需要线程索引才能让终结器看到它的堆栈跟踪吗?还是有其他方法?我是按照这个例子来的 - http://dotnetdebug.net/2005/06/22/blocked-finalizer-thread/ - Dan H
输入 ~ 命令,您将看到所有活动线程。查找 2 Id: <pid>.19e8 或尝试:~2s;kb - Kjell Gunnar
1个回答

1
你看到的前三列是:
  • WinDbg线程ID。你可以在~xs命令中使用它,其中x是线程的ID。
  • CLR线程ID,对应于.NET堆上的Thread对象(尝试在线程对象上运行!dumpheap -type Thread,然后运行!do,查看m_ManagedThreadId)。我从未找到过真正的用例。
  • 内核线程ID,也由许多其他工具显示,例如任务管理器或Process Explorer。

还有一些特殊情况,似乎你遇到了其中之一。如果第一列显示为XXXX,则.NET没有分配固定的物理操作系统线程。这可能会发生在由.NET运行时管理的线程(例如来自线程池的线程)中。在某个时间点,它可能会再次获得物理线程。

此外,似乎存在.NET仍记得操作系统线程的情况(以下示例的第一行,操作系统ID为f28),以及它不记得的情况(第二和第三行,其中操作系统ID为0)。
XXXX    3  f28 00000087c5f974c0 80030228 Preemptive  00000087C6033180:00000087C6033FD0 00000087c4213450 0     Ukn 
XXXX    4    0 00000087c5fa24b0    39820 Preemptive  0000000000000000:0000000000000000 00000087c4213450 0     MTA 
XXXX    5    0 00000087c5fd0eb0    39820 Preemptive  0000000000000000:0000000000000000 00000087c4213450 0     MTA 

通常,操作系统线程ID很有帮助,因为它与~的输出相关(1098是进程ID)。然而,在我的转储文件中,没有本机ID为f28的线程:
0:009> ~
   0  Id: 1098.564 Suspend: 0 Teb: 000007f5`ffd3d000 Unfrozen
   1  Id: 1098.1e98 Suspend: 0 Teb: 000007f5`ffd3b000 Unfrozen
   2  Id: 1098.1d6c Suspend: 0 Teb: 000007f5`ffd39000 Unfrozen
   3  Id: 1098.458 Suspend: 0 Teb: 000007f5`ffd35000 Unfrozen
   4  Id: 1098.13d4 Suspend: 0 Teb: 000007f5`ffd33000 Unfrozen
   5  Id: 1098.1b20 Suspend: 0 Teb: 000007f5`ffb5e000 Unfrozen
   6  Id: 1098.80c Suspend: 0 Teb: 000007f5`ffb52000 Unfrozen
   7  Id: 1098.944 Suspend: 0 Teb: 000007f5`ffb5c000 Unfrozen
   8  Id: 1098.90 Suspend: 0 Teb: 000007f5`ffb5a000 Unfrozen
.  9  Id: 1098.1738 Suspend: 0 Teb: 000007f5`ffb58000 Unfrozen

你可以检查的一件事是线程状态,在你的情况下报告为80039220。这给我们:

0:009> !ThreadState 80039220
    Legal to Join
    Background
    CLR Owns
    In Multi Threaded Apartment
    Reported Dead
    Fully initialized
    Detached

“报告死亡”似乎证实了你的悲伤。

仅供我理解:.NET终结器线程2在OS线程19e8上运行?如果它死锁了,OP仍然应该能够获取19e8的堆栈跟踪,对吗? - Lieven Keersmaekers
1
@LievenKeersmaekers:我还不知道如何访问没有物理线程的.NET线程的堆栈。我正在尝试找出它在哪里,因为它必须存在于某个地方。通常,寄存器和指令指针作为内核对象的一部分被保存在上下文结构中,但这里不适用。 - Thomas Weller
@DanH:我会尝试找出来。调用堆栈应该在某个地方 :-) - Thomas Weller
@ThomasWeller 我发出了 ~*k 200 命令并搜索了终结器线程,但它不在列表中。这是否确认了已报告死亡的线程状态是正确的? - Dan H
@ThomasWeller 感谢您的帮助。现在,我必须弄清楚它为什么死了。我有一种感觉这会很困难... - Dan H
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接