溫故之.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~

公衆號二維碼
相關文章
相關標籤/搜索