性能监视器- Performance Monitor

柴泰平
2023-12-01

性能监视器是Windows自带的系统资源和性能监视工具. 性能监视器能够量化地提供CPU使用率, 内存分配状况, 异常派发情况, 线程调度频率等信息. ASP.NET能够提供每秒钟的请求数目, 请求响应时间等等. 性能监视器能够监视一段时间内上述资源的利用情况, 提供平均值和峰值.

 

性能监视器有助于获取关于性能的具体指标, 监视问题出现时系统资源的变化情况. 通过检查性能监视器中一些重要计数器的变化情况, 往往能够找到一些比较有用的线索. 比如比较ASP.Net每秒请求数目, 请求响应时间和CPU利用率是否有相同的变化曲线就能看出性能是否跟负载相关.

 

解决性能问题的时候, 往往会让客户添加下面一些计数器进行性能收集.

  • Process object下的所有计数器
  • Processor object下的所有计数器
  • System object下的所有计数器
  • Memory object下的所有计数器
  • 如果客户的程序时.NET程序, 还会添加以.NET开头的object下的所有计数器.
  • 如果客户使用ASP.NET, 还会添加以ASP.NET开头的object下的所有计数器.

分析性能日志的时候, 重点观察下面这些计数器.

 

Process object

=============

Process object中的计数器可以根据目标进程分析内存, CPU, 线程数目和handle数目. 选出问题的目标进程, 然后分析目标进程的下面一些计数器.

 

%Processor Time

-------------------

该计数器是该进程占用CPU资源的指标. 即便进程繁忙的时候, CPU平均占用率应该在80%以内. 如果超过该数值, 可以认为程序发生了高CPU的问题. 另外一种问题是CPU波动幅度大. 虽然平均占用率不高, 但是上下跳动频繁. 在某一个短时间段里面, 会有连续高CPU的情况出现.

 

Handle Count

------------------

该计数器记录了当前进程使用的kernel object handle数量. Kernel object是重要的系统资源. 当程序进入稳定运行状态的时候, Handle Count数量也应该维持在一个稳定的区间. 如果发现Handle Count在整个程序周期内总体趋势连续向上, 应该考虑程序是否有Handle Leak.

 

ID Process

------------------

该计数器记录了目标进程的进程ID. 你可能觉得奇怪, ID有什么好观察的? 进程ID是用来观察程序是否有重启发生. 比如ASP.NET工作进程可能会自动回收. 由于进程名都相同, 所以只有通过进程ID来判断是否有重新启动现象. 如果ID有变化, 那么而看看程序是否发生崩溃或者Recycle.

 

Private Bytes

------------------

该计数器记录了当前通过VirtualAlloc API进行的, Commit了的Memory数量. 无论是直接调用API申请的内存, Heap Manager申请的内存, 还是CLR的managed heap, 都算在里面. 跟Handle Count一样, 如果在整个程序周期内总体趋势连续向上, 说明有Memory Leak.

 

Virtual Bytes

------------------

该计数器记录了当前进程申请成功的用户态总内存地址, 包括DLL/EXE占用的地址和通过VirtualAlloc API进行的, Reserve了的内存地址数量, 所以该计数器应该总大于Private Bytes. 一般说来, Virtual Bytes与Private Bytes的变化大致一致. 由于内存分片的存在, Virtual Bytes与Private Bytes一般保持一个相对稳定的比例关系. 当Virtual Bytes与Private Bytes的比例大于2的时候, 程序往往有比较严重的内存地址分片.

 

Processor object

==============

Processor object记录系统中芯片的负载情况. 由于普通程序并不可以绑定到某个具体的CPU上执行, 所以在多CPU机器上观察Total Instance也就足够了.

 

%Processor Time

----------------------

该计数器跟Process下的%Processor Time的意义一样, 不过这里记录的不是针对具体的某一个进程, 而是整个系统. 通过把该计数器跟Process下的同名计数器一起比较, 就能看出系统的高CPU问题是否是由于单一的某个进程导致的.

 

System object

==============

System object记录系统中一个整体的统计信息. 所以不区分instance. 通过比较System object下的counter和其他counter的变化趋势, 往往能看出一些线索.

 

Context Switch/ sec

--------------------

Context Switch标示了系统中整体线程的调度, 切换频率. 线程切换是开销比较大的操作. 频繁的线程切换回导致大量CPU周期被浪费. 所以当看到高CPU的时候, 一定要与Context Switch一起比较. 如果两者有相同的变化趋势, 高CPU往往是由于contention(线路争夺)导致的, 而不是死循环.

 

Exception Dispatches/ sec

-------------------

Exception Dispatches表示了系统中异常派发, 处理的频繁程度. 跟线程切换一样, 异常处理也需要大量的CPU开销. 分析方法跟Context Swith雷同.

 

File Data Operations/ sec

-------------------

File Data Operations记录了当前系统中磁盘文件读写的频繁程度. 通过观察该计数器跟其他性能指针的变化趋势, 能够判断磁盘文件操作是否是性能瓶颈. 类似的计数器还有Network Interface, Bytes Total/ sec

 

Memory Object

=============

Memory object记录了当前系统中整体内存的统计信息.

 

Avaiable Mbytes 和 Committed Bytes

---------------------

Available Mbytes记录了当前剩余的物理内存数量. Committed Bytes记录了所有进程commit的内存数量. 结合两个计数器可以观察到:

  1. 两者相加可以粗略估计系统总体可用内存的多少, 便于估计物理配置.
  2. 当Available Mbytes少于100MB的时候, 说明系统总体内存紧张, 会影响到系统所有进程的性能. 应该考虑增加物理内存或检查内存泄露.
  3. 通过比较Process object中的Private Bytes和Virtual Bytes, 便于进一步确认是否有内存泄露, 判断内存泄露是否是由某一单个进程导致的.

Free System Page Table Entries, Pool Paged Bytes 和 Pool Nonpaged Bytes

--------------------

这三个计数器可以衡量核心态空闲内存的数量. 特别是当使用/3GB开关后, 核心态内存地址被压缩, 容易导致核心态内存不足, 继而引发一些非常奇怪的问题.

 

.NET CLR Memory object

=============

.NET CLR Memory object记录了CLR进程中跟CLR相关的内存信息. 该类别下的所有计数器都很有趣, 意思也非常直接. 建议用一个例子程序进行测试和研究. 下面是两个最常用的计数器.

 

Bytes in all heaps

----------------------

Bytes in all heaps 记录了上次GC发生时所统计到的, 进程中不能被回收的所有CLR object占用的内存空间. 该计数器不是实时的, 每次GC发生的时候, 该计数器才更新. 与同一进程的Process下的Private Bytes比较, 可以区分出managed heap和native memory的变化情况. 对于memory leak, 便于区分是managed heap的leak, 还是native memory 的leak.

 

%Time in GC

%Time in GC记录了GC发生的频繁程度. 一般来说15%以内算比较正常. 当超过20%时, 说明GC发生过于频繁. 由于GC不仅带来很高的CPU开销, 而且还需要挂起目标进程的CLR线程, 所以高频率GC是非常危险的. 通过跟CPU利用率和其他性能指标的比较, 往往能够看出GC对性能的影响. 高频率的GC往往因为:

  1. 负载过高.
  2. 不合理的架构, 对内存使用率不高.
  3. 内存泄露, 内存碎片导致内存压力.

ASP.NET的性能监视

============

如果目标程序时ASP.NET, 在ASP.NET开头的object中, 下面这些计数器对于测量ASP.NET的性能非常有用. 由于不少计数器存在于多个object 类别中, 下面只列出具体的计数器名字, 而不去对应到具体的object.

 

Application Restarts

-------------------

Application Restarts记录了ASP.NET Application Domain重启的次数. 导致ASP.NET appDomain重启的原因往往是虚拟目录被修改. 比如修改了web.config文件, 或者防毒程序对母你目录进行了扫描. 通过该计数器可以观察是否有异常的重启现象.

 

Request Execution Time

-------------------

Request Execution Time记录了请求的执行时间, 它是衡量ASP.NET性能的最直接参数. 通过该计数器的平均值来衡量性能是否合乎预期. 需要注意的地方是: 由于Windows并非实时系统, 所以不能用峰值来衡量整体性能. 比如当GC发生的时候, 请求执行时间肯定要超过GC的时间. 所以, 平均值才是有效的标准.

 

Request Current

-------------------

Request Current记录了当前正在处理的和等待处理的请求. 最理想的情况是Request Current等于CPU的数量, 这说明请求与硬件资源能并发处理的能力恰好吻合, 硬件投资正运行在最优状态. 但是一般说来, 当负荷比较大的时候, Request Current也随着增高. 如果Request Current在一段时间内有超过10的情况, 说明性能有问题. 注意观察此时对应的CPU情况和其他的资源. 如果CPU不高, 很可能是是程序中有Blocking发生, 比如等待数据库请求, 导致请求无法及时完成.

 

Request/ second

------------------

Request/ second计数器记录了每秒钟到达ASP.NET的请求数. 这是衡量ASP.NET负载的直接参数. 注意观察Request/ second是否超过程序预期的吞吐量. 如果Request/ second 有突发的波动, 注意看是否有拒绝服务攻击. 通过把Request/second, Request Current, Request Execution Time和系统资源一起比较, 往往能够看出ASP.NET整体性能的变化和各个因素之间的影响.

 

Request in Application Queue

------------------

当ASP.NET没有空余的工作线程来处理新进入的请求的时候, 新的请求会被放到Application Queue中. 当Application Queue堆积的请求也超过设定数值的时候, ASP.NET直接返回503 Server Too Busy错误, 同时丢弃该请求. 所以正常情况下, Request Application Queue应该总为0, 否则说明已经有请求堆积, 性能问题严重.

 

摘自<Windows用户态程序高效排错>

 类似资料: