某工控图片上传服务 CPU 爆高分析
一:背景
1.讲故事
今天给大家带来一个入门级的 CPU 爆高案例,前段时间有位朋友找到我,说他的程序间歇性的 CPU 爆高,不知道是啥情况,让我帮忙看下,既然找到我,那就用 WinDbg 看一下。
二:WinDbg 分析
1. CPU 真的爆高吗
其实我一直都在强调,要相信数据,口说无凭,一定要亲自验证一下,可以使用 !tp 命令。
0:000> !tp
CPU utilization: 81%
Worker Thread: Total: 32 Running: 0 Idle: 18 MaxLimit: 2047 MinLimit: 2
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 0 Free: 0 MaxFree: 4 CurrentLimit: 0 MaxLimit: 1000 MinLimit: 2
从卦中可以看到,当前的 CPU=81%,果然爆高无疑,接下来就得调查下为什么会爆高,可以从触发 GC 入手。
2. GC 触发了吗
要观察是否 GC 触发,可以观察下线程列表上是否有 (GC) 字样,比如下面的输出。
0:006> !t
ThreadCount: 38
UnstartedThread: 0
BackgroundThread: 37
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 5f0 01310688 2a020 Preemptive 00000000:00000000 0130aa50 0 MTA
2 2 818 0131e358 2b220 Preemptive 00000000:00000000 0130aa50 0 MTA (Finalizer)
3 6 7b0 01374908 202b220 Preemptive 00000000:00000000 0130aa50 0 MTA
4 7 f98 01381c50 102a220 Preemptive 00000000:00000000 0130aa50 0 MTA (Threadpool Worker)
6 3 610 013eba78 2b220 Cooperative 00000000:00000000 0130aa50 1 MTA (GC)
9 44 e04 05585068 1029220 Preemptive 00000000:00000000 0130aa50 0 MTA (Threadpool Worker)
10 25 448 063dab30 21220 Preemptive 00000000:00000000 0130aa50 0 Ukn
...
从卦中可以看到6号线程果然带了 (GC) 字样,接下来用 kb 观察下到底是哪一代GC。
0:006> kb
# ChildEBP RetAddr Args to Child
00 05beef18 72bb4825 0b771000 00000003 00000001 clr!WKS::gc_heap::relocate_survivor_helper+0x87
01 05beef48 72bb46da 0b771000 00000001 00000000 clr!WKS::gc_heap::relocate_survivors+0x93
02 05beef98 72bb1913 00000000 00000001 73180140 clr!WKS::gc_heap::relocate_phase+0x8b
03 05bef140 72bb0f69 00000000 00000001 00000001 clr!WKS::gc_heap::plan_phase+0x13b8
04 05bef168 72bb12ef 5e7aa9c3 7317fcd0 00000000 clr!WKS::gc_heap::gc1+0xe8
05 05bef1a0 72bb140c 00000040 7317ff04 7317ff04 clr!WKS::gc_heap::garbage_collect+0x447
06 05bef1c8 72bb161c 00000000 00000000 00000040 clr!WKS::GCHeap::GarbageCollectGeneration+0x1fb
07 05bef1ec 72bb1696 7317ff04 71a9d900 00000002 clr!WKS::gc_heap::trigger_gc_for_alloc+0x1e
08 05bef21c 72bff51a 00000000 00000040 0c1c7aa4 clr!WKS::gc_heap::try_allocate_more_space+0x162
09 05bef230 72bff687 00000000 01304d38 72bff140 clr!WKS::gc_heap::allocate_more_space+0x18
0a 05bef24c 72ab4477 013ebab8 00000040 00000002 clr!WKS::GCHeap::Alloc+0x5c
0b 05bef26c 72ab44f5 01000000 71ab5e90 05bef3f8 clr!Alloc+0x87
0c 05bef2b4 72ab4595 5e7aab5f 00000bb8 05bef3f8 clr!AllocateObject+0x99
0d 05bef33c 719b8281 71a2417c 05bef358 05bef35c clr!JIT_New+0x6b
0e 05bef360 7225652d 00000000 00000000 00000000 mscorlib_ni!System.Threading.Tasks.Task.Delay+0x41 [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5885]
0f 05bef454 05a9d18a 00000000 00000000 00000000 mscorlib_ni!System.Threading.Tasks.Task.Delay+0xd [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5843]
...
因为 C++ 默认是 this 协定,从 clr!WKS::gc_heap::plan_phase+0x13b8 方法的第二个参数 00000001 可知,当前触发了 1 代 GC,其实 1 代 GC 本来就触发频繁,所以问题不大,主要就是看是否为 2 代GC,即 FullGC。
到这里,GC触发的路堵死了,我们就看下是不是还有其他的可疑情况,比如高时钟个数的线程。
3. 有长时间运行线程吗
如果是当事人,可以用 Process Explorer 工具直接观察 Thread 列表的 Cycles Delta 列就能知道,比如下面的百度云管家,

可以看到 11156 号线程占用了太多的时钟周期个数,可惜我不是当事人,所以只能用 cpuid 命令观察。
0:006> !runaway
User Mode Time
Thread Time
6:610 0 days 0:47:07.984
10:448 0 days 0:11:32.531
12:17d4 0 days 0:01:34.265
9:e04 0 days 0:01:29.468
11:16ec 0 days 0:01:11.562
13:1458 0 days 0:01:07.703
...
从卦中可以看到,6号线程耗费的时钟个数遥遥领先,甩了第二名 10 号线程几条街,这个线程非常可疑,得好好研究下它的托管栈了。
0:006> !clrstack
OS Thread Id: 0x610 (6)
Child SP IP Call Site
05bef2d0 72bb47ae [HelperMethodFrame: 05bef2d0]
05bef344 719b8281 System.Threading.Tasks.Task.Delay(Int32, System.Threading.CancellationToken) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5885]
05bef36c 7225652d System.Threading.Tasks.Task.Delay(Int32) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5843]
05bef370 05a9d18a xxx.Api.Core.xxx+c__DisplayClass2_0.<.cctor>b__0()
05bef45c 719b7118 System.Threading.Tasks.Task.InnerInvoke() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2884]
05bef468 719b6cc0 System.Threading.Tasks.Task.Execute() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2498]
05bef48c 719b70ea System.Threading.Tasks.Task.ExecutionContextCallback(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2861]
05bef490 719d40c5 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 954]
05bef4fc 719d3fd6 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 902]
05bef510 719b6f68 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2827]
05bef574 719b6e72 System.Threading.Tasks.Task.ExecuteEntry(Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2756]
05bef584 71a2acbc System.Threading.Tasks.ThreadPoolTaskScheduler.LongRunningThreadWork(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\ThreadPoolTaskScheduler.cs @ 49]
05bef588 719a70e3 System.Threading.ThreadHelper.ThreadStart_Context(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ 74]
05bef594 719d40c5 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 954]
05bef600 719d3fd6 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 902]
05bef614 719d3f91 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 891]
05bef62c 71a28cae System.Threading.ThreadHelper.ThreadStart(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ 93]
05bef770 72a90096 [GCFrame: 05bef770]
05bef954 72a90096 [DebuggerU2MCatchHandlerFrame: 05bef954]
从线程栈上看还好有一个托管方法 xxx.Api.Core.xxx+c__DisplayClass2_0.<.cctor>b__0 ,接下来观察下源码,修剪后的代码如下:
static xxxUploadPool()
{
_queue = new ConcurrentQueue<xxxModel>();
_xxx = new xxxService();
int second = Configuration.xxx * 1000;
Task.Factory.StartNew(delegate
{
while (true)
{
lock (_queue)
{
if (_queue.Count > 0 && _queue.TryDequeue(out var result))
{
_xxx.UploadFilexxxx(result._path, result._repositoryName, xxx);
}
}
Task.Delay(second);
}
}, TaskCreationOptions.LongRunning);
}
这段代码很有意思,它的本来想法就是开启一个长线程,然后在长线程中不断的轮询等待,问题就出在了这个等待上, 即 Task.Delay(second); 这句, 这句代码起不到任何作用,而且一旦 _queue 中的数据为空就成了死循环, 给 CPU 打满埋下了祸根。
这里有一个疑问:一个线程能把 CPU 打满,那太瞧不起CPU 了,肯定是有对等的 core 个数的线程一起发力,打爆CPU,那如何验证? 观察下 CPU 的个数。
0:006> !cpuid
CP F/M/S Manufacturer MHz
0 6,85,4 GenuineIntel 3193
1 6,85,4 GenuineIntel 3193
也就说只要有两个线程进入了 xxxUploadPool 那就够了,现象也正是如此。
三:总结
这段代码确实很有意思,猜测原来就是 Thread.Sleep(second) ,但为了赶潮流改成了 Task.Delay(second),在不清楚后者的使用场景下给 CPU 间歇性爆高埋下了祸根,所以大家在使用新的语法时,一定要弄清楚场景,万不可生搬硬套。

某工控图片上传服务 CPU 爆高分析的更多相关文章
- TCP客户端图片上传服务端保存本地示例
//TCP客户端public class TCPClient { public static void main(String[] args)throws IOException { Socket s ...
- 微信小程序开发之多图片上传+服务端接收
前言: 业务需求,这次需要做一个小程序同时选中三张图片一起上传到服务端,后端使用的.NET WEBAPI接收数据保存. 使用技术: 在这章中将会使用到微信小程序wx.uploadFile(Object ...
- [转]微信小程序开发(二)图片上传+服务端接收
本文转自:http://blog.csdn.net/sk719887916/article/details/54312573 文/YXJ 地址:http://blog.csdn.net/sk71988 ...
- 微信小程序---图片上传+服务端接受
原文地址:http://blog.csdn.net/sk719887916/article/details/54312573 微信小程序,图片上传,应用地方-修改用户信息的头像. 详细代码: 小程序的 ...
- 记一次 .NET 车联网云端服务 CPU爆高分析
一:背景 1. 讲故事 前几天有位朋友wx求助,它的程序CPU经常飙满,没找到原因,希望帮忙看一下. 这些天连续接到几个cpu爆高的dump,都看烦了,希望后面再来几个其他方面的dump,从沟通上看, ...
- 记一次 .NET 某智能交通后台服务 CPU爆高分析
一:背景 1. 讲故事 前天有位朋友加微信求助他的程序出现了CPU爆高的问题,开局就是一个红包,把我吓懵了! 由于是南方小年,我在老家张罗处理起来不方便,没有第一时间帮他处理,朋友在第二天上午已经找出 ...
- Asp.NetCoreWebApi图片上传接口(二)集成IdentityServer4授权访问(附源码)
写在前面 本文地址:http://www.cnblogs.com/yilezhu/p/9315644.html 作者:yilezhu 上一篇关于Asp.Net Core Web Api图片上传的文章使 ...
- 利用layui的load模块解决图片上传
首先肯定要参考layui官网的upload模块文档:http://www.layui.com/doc/modules/upload.html 讲讲思路:在一份添加表单中,我们有个图片上传的模块,然后我 ...
- 分享一个react 图片上传组件 支持OSS 七牛云
react-uplod-img 是一个基于 React antd组件的图片上传组件 支持oss qiniu等服务端自定义获取签名,批量上传, 预览, 删除, 排序等功能 需要 react 版本大于 v ...
- java+Word图片上传控件
这种方法是servlet,编写好在web.xml里配置servlet-class和servlet-mapping即可使用 后台(服务端)java服务代码:(上传至ROOT/lqxcPics文件夹下) ...
随机推荐
- java代码审计的点
java代码审计的点 组件的审计 首先看pom.xml查看第三方组件和第三方组件的版本 常用的第三方组件: 第三方组件 漏洞类型 组件漏洞版本 log4j2 远程代码执行 Apache log4j2 ...
- Java开发学习(三十)----Maven聚合和继承解析
一.聚合 分模块开发后,需要将这四个项目都安装到本地仓库,目前我们只能通过项目Maven面板的install来安装,并且需要安装四个,如果我们的项目足够多,那么一个个安装起来还是比较麻烦的 如果四个项 ...
- KingbaseES 逻辑备份还原加密
KingbaseEs 支持在sys_dump备份时使用key进行加密.在sys_restore时,如果没提供key,或者key值不对,将无法进行恢复. [kingbase@dbhost03 ~]$ s ...
- 【读书笔记】C#高级编程 第十五章 反射
(一)在运行期间处理和检查代码 自定义特性允许把自定义元数据与程序元素关联起来.反射是一个普通术语,它描述了在运行过程中检查和处理程序元素的功能.例如,反射允许完成的任务: 枚举类型的成员 实例化新对 ...
- 部署Zabbix4.0和Grafana
部署Zabbix4.0和Grafana 一.Zabbix 1.安装 rpm -Uvh https://repo.zabbix.com/zabbix/4.0/rhel/7/x86_64/zabbix-r ...
- [LeetCode]-217.存在重复元素-简单
217. 存在重复元素 给定一个整数数组,判断是否存在重复元素. 如果存在一值在数组中出现至少两次,函数返回 true .如果数组中每个元素都不相同,则返回 false . 示例 1: 输入: [1, ...
- Gitea 与 Drone 集成实践:完全基于 Docker 搭建的轻量级 CI/CD 系统
Drone 是一个使用 Go 语言编写的自助式的持续集成平台,和 Gitea 一样可以完全基于容器部署,轻松扩展流水线规模.开发者只需要将持续集成过程通过简单的 YAML 语法写入 Gitea 仓库目 ...
- Java语言(基础一)
Java语言 Java的特性和优势 简单性(简单易学) 面向对象(一种思想 万物皆对象) 可移植性(一次编写到处运行 JVM) 高性能(及时编译) 分布式(网络分布式url) 动态性(反射机制) 多线 ...
- 查看pod创建时使用yaml文件内容
除了 kubectl describe pod 以外,另一种获取 Pod 额外信息(除了 kubectl get pod)的方法 是给 kubectl get pod 增加 -o yaml 输出格式参 ...
- 9_SpringBoot
一. SpringBoot介绍 1.1. 引言 为了使用SSM框架去开发, 准备SSM框架的模板配置 为了使Spring整合第三方框架, 单独的去编写xml文件 导致SSM项目后期xml文件特别多, ...