.NET定位CPU使用率过高问题
摘要:
当一个.net应用在生产环境CPU突然居高不下,如何快速准确的定位问题所在,并且对实时业务影响最小化?如何不抓Dump也不用live debug就可以知道你的应用在做什么?如何确认你的应用是由于哪个线程的执行造成的CPU升高,该线程正在执行什么代码?
分析:
CPU升高的原因有很多,
1、有时候应用的负载大了,CPU自然会受业务请求的增加和增高;
2、有时候因为GC回收使用了过高的CPU资源;
3、有时候是某个线程执行的代码在某种情况下陷入了死循环;
4、有时候是因为锁争用太激烈,某资源上的锁释放后,等待的线程去抢锁引起的;
5、有时候是因为线程太多,上下文切换太频繁引起的。
6、每秒抛出太多的Exception。
我们一一分析
1、我们一般会用一些计数器来观察实际的应用的负载情况和并发请求量,比如每秒接受多少请求等,所以业务量增大引起的CPU高,很容易确定。
2、GC使用的CPU百分比有专门的计数器,一看便知。
3、如果某段代码陷入了死循环引起的CPU高,只抓Dump看~*e!clrstack和!runaway还是不太好定位问题,
a)、一般都是连续抓几个dump,然后用!runaway来看哪些线程的用户态时间的差很大,然后再去看该线程的调用栈。
b)、录制Thread\Thread Id和Thread\% Processor Time计数器,同时抓dump,从计数器里找到CPU消耗高的线程ID,然后从dump里看调用栈和调用栈的参数本地变量等。
4、锁争用也有相关的.NET计数器可以直接看。
5、每个进程的线程数,每秒上下文切换次数也可以有直接的计数器可看。
6、每秒抛出的异常也有直接的计数器可看。
思路:
1、从上面看到也就是第3种地排查比较费劲,而且抓DUMP有时候容易把服务抓S,如果是一个有状态的服务,抓死服务后果很严重,所以我们得想出一种更轻量级的方法去获取服务的每个线程的调用栈。其实CLR本身有一些用于支持调试的接口,都是Com的,但.NET对此有一些包装,可以用c#来使用这些调试API,其中当然包括附加到进程,获取所有线程调用栈的功能。该DLL在.net sdk里,叫MdbgCore.dll。
2、另外获取计数器.NET也有现成的类,上篇帖子介绍过了。
3、.NET对进程的管理也有一些API,可以获取一个进程的线程数,每个线程的启动时间,用户态时间,线程状态,优先级等信息。
有了以上几个知识点,我们就可以综合起来写一个比较智能化定位高CPU问题的工具。
CPU高的DEMO
我们先写一个CPU高的DEMO,A方法因为有sleep所以不会太消耗CPU,而B方法没有Sleep,执行一个浮点运算,所以会造成CPU升高(占用一个CPU的资源)。
using System;
using System.Threading; namespace HightCPUDemo
{
internal class Program
{
private static void Main(string[] args)
{
new Thread(A).Start();
new Thread(B).Start();
Console.ReadKey();
} private static void A(object state)
{
while (true)
{
Thread.Sleep();
}
} private static void B(object state)
{
while (true)
{
double d = new Random().NextDouble()*new Random().NextDouble();
}
}
}
}
代码实现
我们的目标在该程序运行的时候,找出B方法,并确认它就是引起CPU高度原因,代码如下,不太想解释了,代码不复杂,重在思路。
完整代码实现
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using Microsoft.Samples.Debugging.MdbgEngine; internal class MyThreadInfo
{
public string CallStack = "null";
public string Id;
public string ProcessorTimePercentage;
public string StartTime;
public string UserProcessorTime; public override string ToString()
{
return
string.Format(
@"<table style=""width: 1000px;""><tr><td style=""width: 80px;"">ThreadId</td><td style=""width: 200px;"">{0}</td><td style=""width: 140px;"">% Processor Time</td><td>{1}</td></tr>
<tr><td style=""width: 80px;"">UserProcessorTime</td><td style=""width: 200px;"">{2}</td><td style=""width: 140px;"">StartTime</td><td>{3}</td></tr><tr><td colspan=""4"">{4}</td></tr></table>",
Id, ProcessorTimePercentage, UserProcessorTime, StartTime, CallStack);
}
} internal class MyThreadCounterInfo
{
public PerformanceCounter IdCounter;
public PerformanceCounter ProcessorTimeCounter; public MyThreadCounterInfo(PerformanceCounter counter1, PerformanceCounter counter2)
{
IdCounter = counter1;
ProcessorTimeCounter = counter2;
}
} internal class Program
{
// Skip past fake attach events.
private static void DrainAttach(MDbgEngine debugger, MDbgProcess proc)
{
bool fOldStatus = debugger.Options.StopOnNewThread;
debugger.Options.StopOnNewThread = false; // skip while waiting for AttachComplete proc.Go().WaitOne();
Debug.Assert(proc.StopReason is AttachCompleteStopReason); debugger.Options.StopOnNewThread = true; // needed for attach= true; // needed for attach // Drain the rest of the thread create events.
while (proc.CorProcess.HasQueuedCallbacks(null))
{
proc.Go().WaitOne();
Debug.Assert(proc.StopReason is ThreadCreatedStopReason);
} debugger.Options.StopOnNewThread = fOldStatus;
} // Expects 1 arg, the pid as a decimal string
private static void Main(string[] args)
{
try
{
int pid = int.Parse(args[]); var sb = new StringBuilder();
Process process = Process.GetProcessById(pid);
var counters = new Dictionary<string, MyThreadCounterInfo>();
var threadInfos = new Dictionary<string, MyThreadInfo>(); sb.AppendFormat(
@"<html><head><title>{0}</title><style type=""text/css"">table, td{{border: 1px solid #000;border-collapse: collapse;}}</style></head><body>",
process.ProcessName); Console.WriteLine("1、正在收集计数器"); var cate = new PerformanceCounterCategory("Thread");
string[] instances = cate.GetInstanceNames();
foreach (string instance in instances)
{
if (instance.StartsWith(process.ProcessName, StringComparison.CurrentCultureIgnoreCase))
{
var counter1 =
new PerformanceCounter("Thread", "ID Thread", instance, true);
var counter2 =
new PerformanceCounter("Thread", "% Processor Time", instance, true);
counters.Add(instance, new MyThreadCounterInfo(counter1, counter2));
}
} foreach (var pair in counters)
{
pair.Value.IdCounter.NextValue();
pair.Value.ProcessorTimeCounter.NextValue();
}
Thread.Sleep();
foreach (var pair in counters)
{
try
{
var info = new MyThreadInfo();
info.Id = pair.Value.IdCounter.NextValue().ToString();
info.ProcessorTimePercentage = pair.Value.ProcessorTimeCounter.NextValue().ToString("0.0"); threadInfos.Add(info.Id, info);
}
catch
{
}
} Console.WriteLine("2、正在收集线程信息");
ProcessThreadCollection collection = process.Threads;
foreach (ProcessThread thread in collection)
{
try
{
MyThreadInfo info;
if (threadInfos.TryGetValue(thread.Id.ToString(), out info))
{
info.UserProcessorTime = thread.UserProcessorTime.ToString();
info.StartTime = thread.StartTime.ToString();
}
}
catch
{
}
} var debugger = new MDbgEngine(); MDbgProcess proc = null;
try
{
proc = debugger.Attach(pid);
DrainAttach(debugger, proc); MDbgThreadCollection tc = proc.Threads;
Console.WriteLine("3、正在附加到进程{0}获取调用栈", pid);
foreach (MDbgThread t in tc)
{
var tempStrs = new StringBuilder();
foreach (MDbgFrame f in t.Frames)
{
tempStrs.AppendFormat("<br />" + f);
}
MyThreadInfo info;
if (threadInfos.TryGetValue(t.Id.ToString(), out info))
{
info.CallStack = tempStrs.Length == ? "no managment call stack" : tempStrs.ToString();
}
}
}
finally
{
if (proc != null)
{
proc.Detach().WaitOne();
}
}
foreach (var info in threadInfos)
{
sb.Append(info.Value.ToString());
sb.Append("<hr />");
}
sb.Append("</body></html>"); Console.WriteLine("4、正在生成报表");
using (var sw = new StreamWriter(process.ProcessName + ".htm", false,
Encoding.Default))
{
sw.Write(sb.ToString());
} Process.Start(process.ProcessName + ".htm");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}单元测试
找出HeightCPUDemo的进程ID,比如是8724,然后执行PrintStack.exe 8724,输出结果如下
E:\study\ThreadStack\PrintStack\bin\Debug>PrintStack.exe 8724
1、正在收集计数器...
2、正在收集线程信息...
3、正在附加到进程8724获取调用栈...
4、正在生成报表...
最终会在当前目录生成一个HightCPUDemo.htm的报表,其中哪个线程耗费了很多的CPU,及其托管调用栈一目了然,很快就能定位问题。

本文转自:http://www.cnblogs.com/xgw2004058/archive/2011/11/01/2232026.html
原文DEMO有些问题,附上调整后得DEMO下载地址:点我下载
注意一点:项目默认正对X64平台生成,如果需要调试32位需要修改目标平台。
.NET定位CPU使用率过高问题的更多相关文章
- CPU使用率过高分析方法
项目过程中发现,应用服务器经常会出现CPU使用率较高的情况,需要定位出具体代码问题. 1.用top命令,根据CPU使用率排序,找出消耗cpu最高的进程 2.找出该进程下消耗CPU最高的线程(命令:to ...
- 线上cpu使用率过高解决方案
一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. 下面我们将一步步定位问题,详尽的介绍每一步骤的相关知识. 一.通过top命令定位占用cpu高的进程 执行top命令得到 ...
- 06 案例篇:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?
上一节我讲了 CPU 使用率是什么,并通过一个案例教你使用 top.vmstat.pidstat 等工具,排查高 CPU 使用率的进程,然后再使用 perf top 工具,定位应用内部函数的问题.不过 ...
- 4 系统的 CPU 使用率很高,但为啥却找不到高 CPU的应用?
上一节讲了 CPU 使用率是什么,并通过一个案例教你使用 top.vmstat.pidstat 等工具,排查高 CPU 使用率的进程,然后再使用 perf top 工具,定位应用内部函数的问题.不过就 ...
- 服务器CPU使用率过高排查与解决思路
发现服务器的cpu使用率特别高 排查思路: -使用top或者mpstat查看cpu的使用情况# mpstat -P ALL 2 1Linux 2.6.32-358.el6.x86_64 (linux— ...
- 《Troubleshooting SQL Server》读书笔记-CPU使用率过高(下)
<Troubleshooting SQL Server>读书笔记-CPU使用率过高(下) 第三章 High CPU Utilization. CPU使用率过高的常见原因 查询优化器会尽量从 ...
- Oracle查询语句导致CPU使用率过高问题处理
解决此问题的关键在于如何找到造成CPU使用率过高的SQL语句.步骤如下: 1.使用Process Explorer工具查看到Oracle进程,双击Oracle进程,在弹出的属性窗口的Threads选项 ...
- Java程序CPU使用率过高
Java程序CPU使用率过高 通过top命令找到使用率过高的java进程PID 根据进程号查找线程TID:ps -mp PID -o THREAD,tid,time 将TID转换成16进制:print ...
- 记录一次mysql查询速度慢造成CPU使用率很高情况
1.某日zabbix告警,某台机器CPU使用率过高. 查看慢查询日志,看到很多sql语句都超过10秒 把sql语句拿出来放在查询窗口执行.前面加上explain就可以查看详细查询信息 playcode ...
随机推荐
- 推荐一个基于Vue2.0的的一款移动端开发的UI框架,特别好用。。。
一丶YDUI 一只注重审美,且性能高效的移动端&微信UI. 下面为地址自己研究去吧! 我的项目正在用,以前用的Mint-ui但是现在感觉还是这个好一点,官方给出的解释很清楚,很实用. 官方地址 ...
- js-ES6学习笔记-Set结构和Map结构
http://www.cnblogs.com/lonhon/ 1.ES6 提供了新的数据结构 Set.它类似于数组,但是成员的值都是唯一的,没有重复的值. Set 本身是一个构造函数,用来生成 Set ...
- 本地服务器 windows server 2008 datacenter conn /as sysdba 提示 ora-01031 insufficient privileges
原因是需要把当前用户administrator(为例)添加到ora_dba组里. 服务器管理器--配置--本地用户和组--组
- JavaWeb 后端 <一> 之 Tomcat服务器 - Http协议 学习笔记
1.Web开发概述 1.1服务器上的资源分类: a.静态资源:指web页面中供人们浏览的数据始终是不变.html css js 图片 多媒体 b.动态资源:指web页面中供人们浏览的数据是由程序产生的 ...
- win7 点IE浏览器无法打开
现象:win7系统 打开未响应, 装其他浏览器可以使用,但很慢,且有密码框输入不了 处理: WIN7删除添加程序中,选打开或关闭windows功能,其中就有IE11的勾选了, 然后清除勾选,即会出现提 ...
- 访问Access日期字段
在使用sql访问Access日期字段,应在变量前后加#,例子: s:=Format(select xueshID,name,times,qukbz,skdate,banji from dianmjil ...
- java基础,流程控制语句
流程控制语句 条件语句: if语句: *if(条件 boolean类型) ...
- Ext.Component事件
组件类提供了许多生命周期事件.当组件创建时,这些激活,渲染,销毁等等事件被激活.所有这些事件都可以通过使用监听器属性或使用on方法来进行处理.大多数这些生命周期事件实际上都是在ext.abstract ...
- JMeter 监控和记录&常用功能
使用https连接时,如果对应站点的CA 证书错误,会直接报连接不到服务器的错误,org.apache.commons.httpclient.NoHttpResponseException,把错误证书 ...
- 初学安卓开发随笔之 Intent 用法
首先,对于安卓开发,目前世界上流行的是使用的是Android studio 2.0 .(hh 学着来呗 书上说用这个,,) 今后就定一个计划 每天更新一个Android 随笔,增强一下自控力吧!!! ...