完成端口线程的 OutOfMemoryException

Posted

技术标签:

【中文标题】完成端口线程的 OutOfMemoryException【英文标题】:OutOfMemoryException from Completion Port Thread 【发布时间】:2018-07-22 19:40:39 【问题描述】:

我正在处理来自客户的核心转储。因此,我将无法共享导致错误的代码片段。不过,我希望有人能对如何进一步进行一些提示。

我有一个通过套接字执行通信的应用程序。它打开到多个客户端的连接数。一段时间后,它失败并出现 OutOfMemoryException,即使服务器上有足够的内存并且进程没有超过 32 位的限制。

核心转储中的线程池状态如下所示:

!ThreadPool
CPU utilization: 17%
Worker Thread: Total: 16 Running: 0 Idle: 16 MaxLimit: 1023 MinLimit: 16
Work Request in Queue: 0
--------------------------------------
Number of Timers: 2
--------------------------------------
Completion Port Thread:Total: 29 Free: 0 MaxFree: 32 CurrentLimit: 29 
MaxLimit: 1000 MinLimit: 16

29 个完成端口线程中的大多数都会抛出 OutOfMemoryException。

!Threads 输出的一部分如下所示:

 0    1 1c548 00390680   2026020 Preemptive  00000000:00000000 0038a7f0 0     STA 
   2    2 2b770 0039db70     2b220 Preemptive  00000000:00000000 0038a7f0 0     MTA (Finalizer) 
   6    3 2c080 003ef588   a029220 Preemptive  00000000:00000000 0038a7f0 0     MTA (Threadpool Completion Port) 
   7    4 22ae4 004067e0   202b020 Preemptive  0F0905F0:00000000 0038a7f0 0     MTA 
  11    5 1ba94 03a79358   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) 
  12    6 3105c 03a7c138   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016c4bc8
  13    7 250f0 03a7c958   a029220 Preemptive  0F5B50BC:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 00ef70fc
  14    8 32d9c 03a7d578   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016c896c
  15    9 3281c 03a7dd98   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016c2f2c
  16   10 1c360 03a7e7c0   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016bc434
  17   11 240ac 03a815f0   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016bb198
  18   12 250fc 03a8fcd0     21220 Preemptive  00000000:00000000 0038a7f0 0     Ukn 
  19   13 30f1c 03ab4fb0   102a220 Preemptive  00000000:00000000 0038a7f0 0     MTA (Threadpool Worker) 
  20   14 1a034 03ae9210   202b020 Preemptive  0F625BB8:00000000 03a99928 0     MTA 
  21   15 1f3bc 03ae49b8   202b220 Preemptive  00000000:00000000 03a99928 1     MTA 
  22   16 f8d8 03aba8c8   202b020 Preemptive  00000000:00000000 03a99928 0     MTA 
  23   17 32808 03b10118   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016bb760
  24   18 2ee1c 03b0fbd0   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016bc268
  25   19 539c 03b0ebf8   a029220 Preemptive  00000000:00000000 03a99928 0     MTA (Threadpool Completion Port) System.OutOfMemoryException 016c6288
  26   20 31be8 03b0f140   202b220 Preemptive  00000000:00000000 03a99928 1     MTA 
  27   21 2e884 03b10660   202b220 Preemptive  00000000:00000000 03a99928 1     MTA 
  28   22 10934 03b0f688   202b220 Preemptive  00000000:00000000 03a99928 1     MTA 

对我来说,这看起来像是一个死锁,消费者停止处理传入套接字的数据,但这只是一个模糊的理论。

你对此有什么想法吗?

【问题讨论】:

虽然this 特定于 ASP.NET,但很多建议(如堆碎片)实际上普遍适用。您真正需要的是完整的用户模式转储(因此包括托管堆中的所有内容),然后应用已知技术来分析 OOM。您似乎已经精通调试器,所以这应该不是一个大问题。这是一个很好的flowchart,可以帮助您入门。 另外,显然,首先对失败的线程进行堆栈跟踪。有可能一个通用的“资源不足”或“无效参数”错误代码被转换为“内存不足”,即使 memory 实际上并不是我们用完的资源(但是例如,某个地方的内核池的一小部分,或固定的句柄缓冲区,或OVERLAPPED 池,或设计者想出的其他任何东西)。找到失败的呼叫将对此有所帮助。 一个好的开始是运行!dumpheap -stat 并查看托管内存使用了多少总空间,包括可用空间。最后一点很重要,因为它可以帮助您评估在碎片中丢失了多少内存空间(如果应用程序执行大量 LOH 分配,这可能是一个问题) 【参考方案1】:

您提到了 x86。由于旧的决定,x86 .NET 应用程序被限制为用户模式虚拟地址空间的大约 2 GiB RAM: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366778.aspx#memory_limits

有一个开关可以使用 IMAGE_FILE_LARGE_ADDRESS_AWARE 将限制增加到 3 甚至 4 GiB

.NET 内存管理和测量还有其他怪癖,您应该注意:

Do not measure with TaskManager. How the GC works。 Multithreaded servers 需要格外小心。

【讨论】:

正如我在问题中提到的,应用程序进程不超过 32 位 (2 GiB) 的限制。奇怪的是 OutOfMemoryException 仅从 Completion Port Threads 抛出 @Midi 它不超过 2 GiB,因为它未能尝试超过 2 GiB。这就是 OOM 告诉您的:由于碎片化或限制,您必须超过 2 GiB(或 3 或 4)。威奇不工作。而是抛出 OOM。

以上是关于完成端口线程的 OutOfMemoryException的主要内容,如果未能解决你的问题,请参考以下文章

完成端口

重叠 I/O:如何在完成端口事件或正常事件上唤醒线程?

IOCP 是不是创建自己的线程?

完成端口线程的 OutOfMemoryException

在 .NET / .NET Core 中的异步 I/O 期间,线程池的完成端口线程如何表现?

用于小型单线程应用程序的 I/O 完成端口?