温故之.NET性能分析

此文包含如下内容编程

  • 运行时分析
  • 性能计数器
  • 其余性能分析工具

运行时分析

使用perfmon.exe跟踪性能,即性能监视器(Performance Monitor)安全

它是Windows自带的一款分析.NET应用程序的工具,它以图形的方式来表示从内存管理到JIT性能的方方面面。经过它,咱们也能够知道咱们应用程序所使用的资源的状况。服务器

经过 Win + R 的方式,唤出命令提示符工具。在输入框中输入 perfmon.exe(在Windows10里面,若是在开始菜单出输入,必定要输入所有字符,不然敲回车时可能运行的是系统推荐的应用),而后回车便可,以下:网络

Win + R

回车后:数据结构

性能监视器
而后,点击左侧栏中【监视工具】下的【性能监视器】, 获得以下图形:

性能监视器

点击右侧面板中的“绿色加号”,以添加计数器,以下 多线程

添加计数器

能够看到,.NET Framework 应用程序有许多预约义的对象,包括用于内存管理(.NET CLR Memory)、互操做性(.NET CLR Interop)、异常处理(.NET CLR Exceptions)以及多线程处理(.NET CLR LocksAndThreads)的计数器等多种工具。框架

点击列表中项目右边的“向下箭头”,能够查看该性能对象可以支持的计数器dom

单击要查看的性能计数器。工具

在《选定对象的实例》列表框中,单击<全部实例>,指定要在全局(也就是在整个系统范围内)监视CLR的性能计数器。也能够在《选定对象的实例》列表框中,单击要监视该应用程序的性能计数器的应用程序的名称。性能

选择了对象实例以后,点击对话框右下方的【肯定】按钮,既能够完成添加。

步骤以下图:

步骤

以编程方式读取和建立性能计数器

除了以上经过其余工具的方式外,咱们也能够在某些状况下(好比只须要测试某一段代码的性能的时候)使用代码的方式来处理,这种状况下,用代码或许更加高效。

.NET 为咱们提供了如下类(命名空间System.Diagnostics)来作这些事情

  • PerformanceCounter:表示 Windows NT 性能计数器组件。使用此类可读取现有的计数器,或自定义更合适的计数器。也能够向自定义计数器写入性能数据
  • PerformanceCounterCategory: 提供与计数器交互的一些方法
  • PerformanceCounterInstaller:用于指定 PerformanceCounter 组件的安装程序
  • PerformanceCounterType:指定PerformanceCounter计算下一个值(NextValue)使用的公式

示例代码以下:

using System;
using System.Collections;
using System.Diagnostics;

public class App {
    private static PerformanceCounter avgCounter64Sample;
    private static PerformanceCounter avgCounter64SampleBase;

    public static void Main() {
        ArrayList samplesList = new ArrayList();

        /// 用于建立目录/类别
        /// 若是不存在,则须要建立,此时不能当即建立计数器,须要过一下子才能使用
        if (SetupCategory())
            return;
        CreateCounters();
        CollectSamples(samplesList);
        CalculateResults(samplesList);
    }

    private static bool SetupCategory() {
        if (!PerformanceCounterCategory.Exists("AverageCounter64SampleCategory")) {
            CounterCreationDataCollection counterDataCollection = new CounterCreationDataCollection();
            // 为目录添加计数器
            CounterCreationData averageCount64 = new CounterCreationData {
                CounterType = PerformanceCounterType.AverageCount64,
                CounterName = "AverageCounter64Sample"
            };
            counterDataCollection.Add(averageCount64);
            CounterCreationData averageCount64Base = new CounterCreationData {
                CounterType = PerformanceCounterType.AverageBase,
                CounterName = "AverageCounter64SampleBase"
            };
            counterDataCollection.Add(averageCount64Base);

            // 建立目录
            PerformanceCounterCategory.Create("AverageCounter64SampleCategory",
                "Demonstrates usage of the AverageCounter64 performance counter type.",
                PerformanceCounterCategoryType.SingleInstance, counterDataCollection);

            return true;
        } else {
            // 目录已经存在,所以咱们能够进行后续步骤
            return false;
        }
    }

    private static void CreateCounters() {
        // 建立计数器
        avgCounter64Sample = new PerformanceCounter("AverageCounter64SampleCategory", "AverageCounter64Sample", false);
        avgCounter64SampleBase = new PerformanceCounter("AverageCounter64SampleCategory", "AverageCounter64SampleBase", false);
        avgCounter64Sample.RawValue = 0;
        avgCounter64SampleBase.RawValue = 0;
    }
    private static void CollectSamples(ArrayList samplesList) {
        Random r = new Random(DateTime.Now.Millisecond);
        for (int j = 0; j < 100; j++) {
            int value = r.Next(1, 10);
            Console.Write(j + " = " + value);

            avgCounter64Sample.IncrementBy(value);
            avgCounter64SampleBase.Increment();

            if ((j % 10) == 9) {
                OutputSample(avgCounter64Sample.NextSample());
                samplesList.Add(avgCounter64Sample.NextSample());
            } else
                Console.WriteLine();

            System.Threading.Thread.Sleep(50);
        }
    }

    private static void CalculateResults(ArrayList samplesList) {
        for (int i = 0; i < (samplesList.Count - 1); i++) {
            // 输出样本信息
            OutputSample((CounterSample)samplesList[i]);
            OutputSample((CounterSample)samplesList[i + 1]);

            // 经过.NET自带方法,计算计数器的值
            float counterValue = CounterSampleCalculator.ComputeCounterValue((CounterSample)samplesList[i], (CounterSample)samplesList[i + 1]);
            Console.WriteLine($".NET computed counter value = {counterValue}");
        }
    }
    
    private static void OutputSample(CounterSample s) {
        Console.WriteLine("\r\nSample values - \r\n");
        Console.WriteLine($" BaseValue = {s.BaseValue}");
        Console.WriteLine($" CounterFrequency = {s.CounterFrequency}");
        Console.WriteLine($" CounterTimeStamp = {s.CounterTimeStamp}");
        Console.WriteLine($" CounterType = {s.CounterType}");
        Console.WriteLine($" RawValue = {s.RawValue}");
        Console.WriteLine($" SystemFrequency = {s.SystemFrequency}");
        Console.WriteLine($" TimeStamp = {s.TimeStamp}");
        Console.WriteLine($" TimeStamp100nSec = {s.TimeStamp100nSec}\r\n");
    }
}
复制代码

性能计数器

这个小节将对这个计数器列表中很经常使用的计数器进行简要说明

计数器

包括如下计数器:

  • 异常性能计数器
  • 互操做性能计数器
  • JIT 性能计数器
  • 加载性能计数器
  • 锁定和线程性能计数器
  • 内存性能计数器
  • 联网性能计数器
  • 安全性能计数器

异常性能计数器

.NET CLR Exceptions类别包含的计数器提供应用程序引起的异常的相关信息

  • 引起的异常数: 从应用程序启动以来引起的异常总数。此数值包括.NET异常和转换为.NET异常的非托管异常(例如,从非托管代码返回的HRESULT在托管代码中会被转换为异常)。
  • 引起的异常数/秒: 显示每秒引起的异常数,它包括已处理和未经处理的异常。此计数器是一个潜在性能问题(若是引起较多数目 >100 的异常)的指示器。
  • 筛选次数/秒:显示每秒执行的 .NET 异常筛选次数。
  • Finally 数量/秒:显示每秒执行的 finally 块的数量。特别注意的是,此计数器只计算有异常执行的 finally 块(即抛出异常以后),不计算正常代码路径上的 finally 块。
  • 捕获的深度/秒:显示从引起异常的帧处处理该异常的帧每秒遍历的堆栈帧数。

互操做性能计数器

.NET CLR Interop(互操做)类别包括的计数器提供应用程序与COM组件、COM+服务和外部类型库交互的相关信息

  • CCW 数目:它显示非托管 COM 代码所引用的托管对象数。CCW 是指正在从非托管 COM 客户端引用的托管对象的代理
  • 封送处理次数:显示从应用程序启动以来,将参数和返回值从托管代码封送至非托管代码(反之亦然)的总次数
  • 存根数:显示由公共语言运行时建立的当前存根数。存根负责在 COM 互操做调用或P-Invoke 调用期间将参数和返回值从托管代码封送至非托管代码(或反之)

JIT 性能计数器

.NET CLR JIT 类别包括的计数器提供的由JIT编译相关信息

  • JIT编译的IL字节数:从应用程序启动以来,由实时 (JIT) 编译器编译的微软中间语言 (MSIL) 字节总数
  • JIT编译的方法数:从应用程序启动以来JIT 编译的方法总数。此计数器不包括预先进行JIT` 编译的方法
  • JIT 所占时间百分比:显示自上次JIT编译阶段以来JIT编译所用运行占用时间的百分比
  • JIT每秒编译的IL字节数:显示每秒JIT编译的MSIL字节数
  • JIT失败数:自应用程序启动以来JIT编译器编译失败的方法的高峰数量

加载性能计数器

.NET CLR Loading(加载)类别包括的计数器提供已加载的程序集、类和应用程序域的相关信息

  • 加载程序堆中的字节数:全部应用程序域中类加载程序当前提交内存大小(以字节为单位)
  • 当前 AppDomain:应用程序中加载的应用程序域数量
  • 当前程序集数:当前运行的应用程序中全部应用程序域范围内已加载的程序集数量。若是程序集以非特定于域的形式从多个应用程序域中加载,则此计数器只递增一次
  • 当前已加载的类:全部程序集中已加载类的数量
  • AppDomain 速率:每秒加载的应用程序域的数量
  • 卸载 AppDomain 的速率:每秒卸载的应用程序域的数量
  • 程序集速率:全部应用程序域范围内每秒加载的程序集数量。若是程序集以非特定于域的形式从多个应用程序域中加载,则此计数器只递增一次
  • 加载类的速率:全部程序集中每秒加载的类的数量
  • 加载失败的速率:每秒加载失败的类的数量。加载失败的缘由有不少,例如安全性不够或格式无效等等
  • 加载失败总数:自应用程序启动以来加载失败的类的峰值
  • AppDomain 总数:自应用程序启动以来已加载的应用程序域的峰值。
  • 卸载的 AppDomain 总数:显示自应用程序启动以来卸载的应用程序的峰值。若是应用程序域加载和卸载屡次,则此计数器将在每次卸载应用程序域时递增。
  • 程序集总数:自应用程序启动以来加载的程序集总数
  • 已加载的类总数:自应用程序启动以来全部程序集中加载的类的累计数量。

锁定和线程性能计数器

.NET CLR LocksAndThreads(锁和线程)类别包括的计数器提供应用程序所使用的托管锁和托管线程的相关信息

  • 当前逻辑线程数目:应用程序中当前托管的线程数。此计数器包括将正在运行和已中止的线程
  • 当前物理线程数目CLR建立和拥有的,用做托管线程的基础线程,的本机操做系统线程数。此计数器的值不包括CLR在其内部操做中使用的线程;它是操做系统进程中线程的子集。
  • 当前已识别的线程数目:当前已由CLR识别的线程数。这些线程与对应的托管线程对象相关联。CLR不建立这些线程,但它们在CLR内至少会运行一次。具备相同线程 ID 的线程不会进行两次计数
  • 已识别的线程总数:自应用程序启动以来已由CLR识别的线程总数
  • 竞争率/秒:运行时中线程尝试获取托管锁的失败率。
  • 当前队列长度:当前正在等待获取应用程序托管锁的线程总数
  • 队列长度/秒:每秒内等待获取应用程序锁的线程数目
  • 队列长度峰值:自应用程序启动以来等待获取托管锁的线程总数峰值。
  • 已识别线程的速率/秒:每秒内由CLR识别的线程数
  • 争用总数:运行中线程尝试获取托管锁失败的总次数

内存性能计数器

.NET CLR Memory(内存)类别包括的计数器提供GC的相关信息

  • 全部堆中的字节数:第 1 代堆、第 2 代堆和大型对象堆的总和,即垃圾回收堆上分配的当前内存(以字节为单位)
  • GC句柄数:正在使用的垃圾回收句柄的数量。垃圾回收句柄:CLR和托管环境外部的资源的句柄
  • 0代回收次数:自应用程序启动以来第0代对象(即最年轻、最近分配的对象)进行垃圾回收的次数
  • 1 代回收次数:自应用程序启动以来第 1 代对象进行垃圾回收的次数
  • 2 代回收次数:自应用程序启动以来第 2 代对象进行垃圾回收的次数
  • 已引起 GC:因显式调用 GC.Collect 而执行的垃圾回收次数峰值
  • 固定对象数目:在上一次垃圾回收中遇到的固定对象(垃圾回收器不能移入内存的对象)的数目
  • 正在使用的同步块数目:正在使用的同步块的数量。 同步块是用于存储同步信息而为每一个对象分配的数据结构,但它不限于只存储同步信息,也能够存储 COM 互操做元数据
  • 已提交的字节总数GC当前已提交的虚拟内存总量(以字节为单位)
  • 已保留的字节总数GC当前保留的虚拟内存量(以字节为单位)
  • GC 所占时间百分比:执行上次垃圾回收与执行垃圾回收所用时间的百分比。此计数器一般指示GC表明应用程序收集和压缩内存所执行的做业
  • 分配的字节数/秒:堆上每秒分配的字节数。注意,此计数器在每次垃圾回收结束时(而非每次分配时)更新
  • 最终存活对象:在终结(Finalize)任务后,仍然存在垃圾回收对象数。 若是这些对象具备对其余对象的引用,则那些对象也会存在,可是不计入此计数器内
  • 0 代堆大小:第 0 代中能够分配的最大字节数;但它不能肯定第 0 代中已分配的字节数
  • 0 代提高的字节数/秒:每秒从第 0 代提高到第 1 代的字节数
  • 1 代堆大小:第 1 代中的当前字节数(切记,此代中的对象不是直接分配的;这些对象是从之前的第 0 代垃圾回收提高的)
  • 1 代提高的字节数/秒:每秒从第 1 代提高到第 2 代的字节数
  • 2 代堆大小:第 2 代中的当前字节数,不包括大对象,切记:此代中的对象(不包括大对象)不是直接分配的
  • 大型对象堆大小:大型对象堆的当前大小。垃圾回收器将大于 85000 字节左右的对象视做大对象而且直接在大对象堆中分配;它们不按照级别来提高
  • 进程 ID:显示被监视的 CLR 进程实例的进程 ID。
  • 从第 0 代提高的终止内存:因为等待终结而从第 0 代提高到第 1 代的内存字节数
  • 从第 0 代提高的内存:垃圾回收后仍存在并从第 0 代提高到第 1 代的内存字节数
  • 从第 1 代提高的内存:显示垃圾回收后仍存在并从第 1 代提高到第 2 代的内存字节数

联网性能计数器

.NET CLR Networking(网络)类别包括的计数器提供应用程序经过网络发送和接收的数据的相关信息

  • 已接收的字节数:自进程启动以来,AppDomain 中的全部 Socket 对象接收到的字节的总数。此数据包括未定义的任何协议信息的TCP/IP数据
  • 已发送的字节数:自进程启动以来,AppDomain 中的全部 Socket 对象已发送的字节的累积总数。此数据包括未定义的任何协议信息的TCP/IP数据
  • 已创建的链接:自进程启动以来,AppDomain 中已经链接的 Socket 对象的累积总数
  • 已接收的数据报:自进程启动以来,AppDomain 中的全部 Socket 对象接收到的数据报包的总数
  • 已发送的数据报:自进程启动以来,AppDomain 中的全部 Socket 对象已发送的数据报包的总数
  • HttpWebRequest 平均生存期:自进程启动以来,AppDomain 中在上一个间隔中结束的全部HttpWebRequest对象的平均时间
  • HttpWebRequest 平均排队时间:自进程启动以来,AppDomain 中在上一个间隔中结束的全部 HttpWebRequest 对象的平均排队时间
  • 建立的 HttpWebRequest/秒AppDomain 中每秒建立的 HttpWebRequest 对象的数目
  • 已排队的 HttpWebRequest/秒AppDomain 中每秒添加到队列的 HttpWebRequest 对象的数量
  • 已停止的 HttpWebRequest/秒AppDomain 中应用程序每秒调用 Abort 方法的 HttpWebRequest 对象的数量。
  • 失败的 HttpWebRequest/秒AppDomain 中每秒从服务器接收失败状态码的 HttpWebRequest 对象的数量。

其余的网络性能计数器,如:

  • 事件计数器:用于测量某些事件的发生次数
  • 数据计数器:用于测量已发送或已接收的数据量
  • 持续时间计数器:测量不一样进程花费的时间。测量对象每一个间隔(一般以秒计)退出不一样状态后的次数
  • 每间隔计数器:用于测量每一个间隔(一般以秒计)中正在进行特定转换的对象数

网络性能计数器包含在两个类别中:

  • .NET CLR 网络:.NET Framework 2 上引入且在 .NET Framework 2 及更高版本上受支持的原始性能计数器。
  • .NET CLR 网络 4:全部上述套接计数器和 .NET Framework 4 及更高版本上受支持的新的性能计数器

安全性能计数器

.NET CLR Security(安全性)类别包括的计数器提供公共语言运行时针对应用程序执行的安全检查的相关信息

  • 连接时检查次数:自应用程序启动以来连接时代码(link-time code)访问安全检查的总次数。 当调用方要求实时 (JIT) 编译时的特定权限时,执行连接时代码访问安全检查
  • RT 检查所占的时间百分比:自上一次取样以来执行运行时代码访问安全检查所用运行时间的百分比
  • 堆栈审核深度:在上次运行时代码访问安全检查期间的堆栈深度
  • 运行时检查总数:自应用程序启动以来执行的运行时代码访问安全检查的总数。当调用方要求特定权限时,执行运行时代码访问安全检查。 运行时检查在调用方每次调用时都会执行,并会检查调用方的当前线程堆栈。 此计数器与“堆栈审阅深度”计数器一块儿使用时可指示安全检查出现的性能损失。

其余性能分析工具

  1. JetBrains dotTrace:它能够帮助你优化应用程序性能指标,支持.NET1.0版本到4.5,快速分析程序瓶颈,找出影响效率的代码。官方网站上有10天试用版
  2. ANTS Performance ProfilerANTS性能分析器是一种用于分析.NET框架支持的用任何语言编写的应用程序的工具。ANTS性能分析器能分析全部.NET应用程序,包括ASP.NET网络应用程序、Windows服务和COM+应用程序。ANTS性能分析器能在几分钟内识别性能瓶颈,运行很是快速,且响应时,对程序的执行具备最低影响。
  3. NET Memory Profiler:一款很是深刻分析.NET内存的优化工具,快速发现内存泄漏问题,而且自动进行内存检测
  4. VS自带的性能分析工具:如图
    Analyze
    点击以后,以下图
    Analyze Options
    能够看到,由CPUGPU、内存及性能向导四个选项,选择您想要进行的性能分析,点击【Start/开始】就能够了

至此,本节内容讲解完毕。欢迎关注公众号【嘿嘿的学习日记】,全部的文章,都会在公众号首发,Thank you~

公众号二维码
相关文章
相关标签/搜索