.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长
一个 asp.net core 站点,之前运行在Linux 服务器上,运行一段时间后有时站点会挂掉,在日志中记录很多“EMFILE too many open files”的错误:
Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -24 EMFILE too many open files
后来将这个 asp.net 站点部署到 Windows 服务器的 IIS 上。运行一段时间后,发现其中一台服务器出现503错误:
HTTP Error 503.2 - Service Unavailable
The serverRuntime@appConcurrentRequestLimit setting is being exceeded.
登上服务器一看,该站点的进程占用的内存竟然有1.2G,而同一负载均衡中另外一台正常的服务器内存占用只有40多M。然后看了一下进程中的线程数,惊呆了——竟然有8000多个线程!而另外一台正常的服务器只有20多个线程。
将这台服务器从负载均衡上摘下来之后,出现了更加让人惊呆的现象——在没有请求的情况下,这个 asp.net core 站点进程的内存占用与线程数一直在增长。就像在代码中写了一个死循环,在循环中不停地创建线程。
再后来内存增长到1.8G左右,线程数增长到1.3万左右,而且还在持续增长。

不仅内存与线程数在增长,而且CPU也一直在波动,这可是在没有任何请求的情况下,谁在偷偷地干活?

强制结束进程后恢复正常,但运行一段时间(通常是1天时间)后又会出现同样的问题。非常奇怪!
从目前分析的情况看,罪魁祸首可能是 EnyimMemcachedCore (支持.net core的memcached客户端,是我们从 EnyimMemcached 移植过来的),EnyimMemcachedCore 用到了 Socket 池,问题可能出在 Socket 池部分,源代码在 github 上(EnyimMemcachedCore源代码)。
windbg分析进程dump文件显示的线程情况:
0:000> .load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.1\sos.dll
0:000> !threads
ThreadCount: 8014
UnstartedThread: 0
BackgroundThread: 8013
PendingThread: 0
DeadThread: 0
Hosted Runtime: no

发现大量线程中存在 coreclr!Thread::DoAppropriateWaitWorker 这个操作:
!uniqstack
# Child-SP RetAddr Call Site
00 00000056`ed5ad118 00007ffa`080e13ed ntdll!NtWaitForMultipleObjects+0xa
01 00000056`ed5ad120 00007ff9`f1dc885e KERNELBASE!WaitForMultipleObjectsEx+0xed
02 00000056`ed5ad400 00007ff9`f1dc8a0d coreclr!Thread::DoAppropriateWaitWorker+0xfe
03 00000056`ed5ad4b0 00007ff9`f1dca52f coreclr!Thread::DoAppropriateWait+0x7d
04 00000056`ed5ad530 00007ff9`f1e3b726 coreclr!CLREventBase::WaitEx+0x7f
05 00000056`ed5ad580 00007ff9`f1e3b636 coreclr!AwareLock::EnterEpilogHelper+0xca
06 00000056`ed5ad640 00007ff9`f1f92b18 coreclr!AwareLock::EnterEpilog+0x62
07 00000056`ed5ad6a0 00007ff9`f1f92131 coreclr!AwareLock::Contention+0x258
08 00000056`ed5ad760 00007ff9`92388e2b coreclr!JITutil_MonContention+0xb1
该问题还在进一步排查中。。。
[12月3日13:00更新]
今天排查后怀疑是 EnyimMemcached 中下面的代码引起的:
private void ConnectWithTimeout(Socket socket, EndPoint endpoint, int timeout)
{
var completed = new AutoResetEvent(false);
var args = new SocketAsyncEventArgs();
args.RemoteEndPoint = endpoint;
args.Completed += OnConnectCompleted;
args.UserToken = completed;
socket.ConnectAsync(args);
if (!completed.WaitOne(timeout) || !socket.Connected)
{
using (socket)
{
throw new TimeoutException("Could not connect to " + endpoint);
}
}
} private void OnConnectCompleted(object sender, SocketAsyncEventArgs args)
{
EventWaitHandle handle = (EventWaitHandle)args.UserToken;
handle.Set();
}
已修改代码以定位是不是上面的代码引起的,要等待下次deadlock的发生。
[12月4日8:50更新]
终于可以重现这个问题,在有负载的情况下强制结束进程,详见录屏。
[12月4日12:20更新]
终于定位到了引起问题的代码:
Task<IPAddress[]> task = System.Net.Dns.GetHostAddressesAsync(host);
task.Wait();
var addresses = task.Result;
这是上次解决 EnyimMemcached 死锁问题 时埋下的坑,死锁发生在有并发请求时进行主机名的解析,在强制结束进程时重现是因为dns解析缓存失效。
改为下面的代码可解决死锁问题:
Task<IPAddress[]> task = System.Net.Dns.GetHostAddressesAsync(host);
if (task.Wait())
{
var addresses = task.Result;
}
虽然死锁问题解决了,但在并发请求下task.Wait(5000)返回false,无法成功解析主机名。
问题的根源是在构造函数中用(且只能用)同步方式调用System.Net.Dns.GetHostAddressesAsync()异步方法。
最终解决方法见:尝试解决.NET Core Framework中Dns.GetHostAddressesAsync()引起的线程死锁
相关链接:
[12月3日16:10更新]
果然是上面的代码引起的死锁,改为下面的代码后问题解决:
private void ConnectWithTimeout(Socket socket, EndPoint endpoint, int timeout)
{
var task = socket.ConnectAsync(endpoint);
if (!task.Wait(timeout))
{
using (socket)
{
throw new TimeoutException("Could not connect to " + endpoint);
}
}
}
.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长的更多相关文章
- 你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题
在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached ...
- 网络瓶颈、线程死锁、内存泄露溢出、栈堆、ajax
网络瓶颈:网络传输性能及稳定性的一些相关元素 线程死锁:多个线程因竞争资源造成的一种僵局 下面我们通过一些实例来说明死锁现象. 先看生活中的一个实例,2个人一起吃饭但是只有一双筷子,2人轮流吃(同时拥 ...
- java命令分析线程死锁以及内存泄漏
一.介绍 jstack是java虚拟机自带的一种堆栈跟踪工具.jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项&qu ...
- 解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题
在我们将站点从 ASP.NET + Windows 迁移至 ASP.NET Core + Linux 的过程中,目前遇到的最大障碍就是 —— 没有可用的支持 .NET Core 的 memcached ...
- 尝试解决在构造函数中同步调用Dns.GetHostAddressesAsync()引起的线程死锁
(最终采用的是方法4) 问题详情见:.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长 看看在 Linux 与 Windows 上发生线程死锁的后果. Linux: Microsoft ...
- ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解 (转载)
“传导体” HttpContext 要理解 HttpContext 是干嘛的,首先,看图 图一 内网访问程序 图二 反向代理访问程序 ASP.NET Core 程序中,Kestrel 是一个基于 li ...
- ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解
笔者没有学 ASP.NET,直接学 ASP.NET Core ,学完 ASP.NET Core MVC 基础后,开始学习 ASP.NET Core 的运行原理.发现应用程序有一个非常主要的 “传导体” ...
- 线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁
当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.E ...
- Innodb之线程独享内存
引用链接: https://blog.csdn.net/miyatang/article/details/54881547 https://blog.csdn.net/wyzxg/article/de ...
随机推荐
- 详解Java 8中Stream类型的“懒”加载
在进入正题之前,我们需要先引入Java 8中Stream类型的两个很重要的操作: 中间和终结操作(Intermediate and Terminal Operation) Stream类型有两种类型的 ...
- (2)WebAPI的增删改查
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Ne ...
- 手机开发中的AP与BP的概念
转自:http://blog.csdn.net/macong01/article/details/15504611 手机的AP和BP: AP:ApplicationProcessor,即应用芯片 BP ...
- Python 爬取网站资源文件
爬虫原理: 以下来自知乎解释 首先你要明白爬虫怎样工作.想象你是一只蜘蛛,现在你被放到了互联“网”上.那么,你需要把所有的网页都看一遍.怎么办呢?没问题呀,你就随便从某个地方开始,比如说人民日报的首页 ...
- ABP中单元测试的技巧:Mock和数据驱动
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:虽然ABP为大家提供了测试的脚手架了,不过有些小技巧还是需要自己探索的. ASP.NE ...
- Eclipse中使用Maven创建web项目
一.创建一个Maven项目 1.Eclipse中用Maven创建项目 上图中点击next 2.继续next 3.选maven-archetype-webapp后,next 4.填写相应的信息,Pack ...
- 用 Blend 给Windows Phone 应用创建 示例数据
前言 创建 示例数据(Sample Data) 是提高程序开发效率的一个很有效方法,有了它,我们调UI的时候就不必每次都运行应用,然后在手机上观看页面效果了,配合 “AlignmentGrid.pn ...
- UWP学习记录10-设计和UI之控件和模式7
UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...
- html内容超出了div的宽度如何换行让内容自动换行
在显示评论列表的时候因为有固定宽,但是显示的内容超出的了div的宽,在这种情况下我们需要将其换行,实现的css代码如下 在工作中评论内容测试遇到评论着的评论内容为:"dddddddddd ...
- 如何实现VoIP中大并发应用
后台服务器实现高并发方式: 说明: 黄色皆为运营商或第三方对接系统的VoIP设备等. 前置服务器A与B为热备容灾模式,当A异常,立即跳转到B. 应用服务器做实时容灾处理. 数据库做实时容灾处理. 媒体 ...