天天看點

【實時】DevExpress記憶體監視

前言

  在做項目的時候,我們有時候需要檢測項目的記憶體占用情況,有時候是檢測記憶體洩露~,有時候是檢視某段代碼執行前後的記憶體對比,以友善找出問題并以解決。

記憶體洩漏也稱作“存儲滲漏”,用動态存儲配置設定函數動态開辟的空間,在使用完畢後未釋放,結果導緻一直占據該記憶體單元。直到程式結束。(其實說白了就是該記憶體空間使用完畢之後未回收)即所謂記憶體洩漏。-百度百科

  前幾天做項目的時候就遇到這種情況,項目是winform開發,大資料壓縮、解壓和綁定的時候,記憶體飙升的很快。雖然通過Windows任務管理器可以檢視到程序的記憶體使用情況,但隻是數值的展現,想要的效果:

【實時】DevExpress記憶體監視

  這種波線圖可以直覺的展現出程式的CPU使用情況,找了一下關于記憶體的,雖然在資源螢幕中有記憶體的使用情況,但是并不是我想要的,也在網上找了下記憶體監視程式,找了大半天也沒找到。

  自己動手,豐衣足食。

DevExpress安裝問題

  這裡在多說句,其實用DevExpress也是沒辦法,網上找了好多圖形控件,但都不是我想要的,雖然DevExpress比較大,但是大有大的好處,那就是功能很強大。

  關于DevExpress的安裝寫了一篇文章《DevExpress控件安裝、漢化使用教程》,大家可以參考下。

  在安裝完之後記得要安裝DevExpress.Patch.13.1.5.exe這個更新檔包,不然開發的程式每次都會彈出:

【實時】DevExpress記憶體監視

  還有一點就是安裝完解除安裝的時候,一定要正确的解除安裝,有次我正在解除安裝的時候強制關機了,重新開機安裝的時候,就會出現這種情況:

【實時】DevExpress記憶體監視

  雖然程式已經解除安裝,但還是提示子產品已經安裝,程式已經被破壞了,然後我就用軟媒 - Win7優化大師(我認為是比較好的清理工具),深度清理了下垃圾檔案和系統資料庫,但是發現還是不行,肯定系統資料庫檔案沒有清理幹淨,有點想要重裝系統的沖動,單僅僅是沖動,沒有行動,畢竟重裝完系統要那麼多的軟體要重新安裝,沒辦法就手動清理系統資料庫吧,搜尋“DevExpress”有關的系統資料庫,那不是一般的多啊,還有些是GUID生成的:

【實時】DevExpress記憶體監視

  一條一條的删啊,也不知道删了多少條,但我記得肯定沒删完,我就是試了下重裝,發現可以了。

  做事要有耐心。

實作

  以上廢話說的有點多,關于DevExpress的教程真的很少(中文),因為DevExpress太龐大了,隻能通過DevExpress提供的示例程式去學習,實作上面波形圖在DevExpress中有個控件叫SwiftPlotDiagram,位置在DevExpress.XtraCharts.v13.1.dll,使用DevExpress實作圖形程式必須包含在ChartControl控件下。

  我們先看下設計器:

【實時】DevExpress記憶體監視

  除去實作記憶體監控這個程式,我們隻考慮波形圖,要實作一般有幾個基本元素:

  •  坐标(SeriesPoint)
  •  時間刻度(TimeInterval)
  •  走勢線(RegressionLine)

RegressionLine翻譯是回歸線的意思,就是一個總的大緻走勢方向,我稱作走勢線,隻是個叫法不同。

  上面元素中坐标最重要,明白了元素我們看下代碼:

1         private const int interval = 20;
 2         private double value = 10.0;
 3         RegressionLine Regression { get { return GetRegressionLine(Series); } }
 4         private int TimeInterval
 5         {
 6             get
 7             {
 8                 return Convert.ToInt32(nud_Interval.Value);
 9             }
10         }
11         private Series Series
12         {
13             get
14             {
15                 return chartControl.Series.Count > 0 ? chartControl.Series[0] : null;
16             }
17         }      

  上面幾個字段就不解釋了,value是縱坐标的值,也就是記憶體值,Series英文翻譯是串聯的意思,這邊表示的是坐标組成曲線的集合,因為這邊我們就畫了一個曲線圖是以是Series[0]。

1         //擷取程序
 2         private void getProcess()
 3         {
 4             foreach (Process item in Process.GetProcesses())
 5             {
 6                 cb_Process.Items.Add(item.ProcessName);
 7             }
 8             cb_Process.SelectedIndex = 0;
 9         }
10         //擷取下一個坐标值
11         private double CalculateNextValue(double value)
12         {
13             Process process = Process.GetProcessesByName(cb_Process.Text)[0];
14             return process.PrivateMemorySize64/1024.0;
15         }      

  getProcess()方法是擷取本地線程集合填充到下拉清單中,CalculateNextValue()方法是更新縱坐标的值,也就是記憶體值,這個方法在timer事件中調用。

  我們看下timer事件代碼:

1         //timer事件
 2         private void timer_Tick(object sender, EventArgs e)
 3         {
 4             if (Series == null )
 5             {
 6                 return;
 7             }
 8             var argument = DateTime.Now;
 9             //一個刻度需要畫的坐标
10             var pointsToUpdate = new SeriesPoint[interval];
11             for (var i = 0; i < interval; i++)
12             {
13                 pointsToUpdate[i] = new SeriesPoint(argument, value);
14                 argument = argument.AddMilliseconds(1);
15                 UpdateValues();
16             }
17             //添加坐标
18             AddPoints(Series, pointsToUpdate);
19             var minDate = argument.AddSeconds(-TimeInterval);
20             //重新設定X軸MinMaxValues
21             var diagram = chartControl.Diagram as SwiftPlotDiagram;
22             if (diagram != null && diagram.AxisX.DateTimeScaleOptions.MeasureUnit == DateTimeMeasureUnit.Millisecond)
23             {
24                 diagram.AxisX.Range.SetMinMaxValues(minDate, argument); 
25             }
26         }      

  timer事件主要做個三個工作,一是根據設定好的interval擷取每個刻度的記憶體值,然後添加到Points數組中,interval的值越大,畫出的波線圖越精細,下面就是把Points數組的坐标值加到Series中,最後把X軸的最大小值設定下,以實作動态的效果,需要注意的是:

1         diagram.AxisX.Range.SetMinMaxValues(minDate, argument);      

  這句代碼的意思不是設定整個X軸的最大小值,而是這個刻度下的最大小值,大家可以把這段代碼注釋下看下效果就知道了,可以看出時間刻度是一樣的,并沒有清除疊加。

【實時】DevExpress記憶體監視

  關于X軸-時間軸,我們可以在設計其中可以設定:

1          swiftPlotDiagram1.AxisX.DateTimeScaleOptions.GridAlignment = DevExpress.XtraCharts.DateTimeGridAlignment.Millisecond;
2          swiftPlotDiagram1.AxisX.DateTimeScaleOptions.MeasureUnit = DevExpress.XtraCharts.DateTimeMeasureUnit.Millisecond;
3          swiftPlotDiagram1.AxisX.Label.DateTimeOptions.Format = DevExpress.XtraCharts.DateTimeFormat.Custom;
4          swiftPlotDiagram1.AxisX.Label.DateTimeOptions.FormatString = "mm:ss";
5          series1.ArgumentScaleType = DevExpress.XtraCharts.ScaleType.DateTime;      

  上面是設定X軸-時間刻度為毫秒,下面是設定顯示的格式,最下面代碼的意思是設定曲線畫圖刻度的格式是DateTime格式,也就是上面SeriesPoint的argument。

  下面關于圖示标注的設定就是圖形上面的标注PrivateMemory,因為所有的圖形控件都內建在ChartControl中,隻要找到設定起來很友善:

1         this.chartControl.Legend.AlignmentHorizontal = DevExpress.XtraCharts.LegendAlignmentHorizontal.Left;
 2         this.chartControl.Legend.AlignmentVertical = DevExpress.XtraCharts.LegendAlignmentVertical.TopOutside;
 3         this.chartControl.Legend.Direction = DevExpress.XtraCharts.LegendDirection.LeftToRight;
 4         this.chartControl.Legend.Visible = true;
 5         series1.Name = "PrivateMemory";
 6         regressionLine1.Name = "Regression Line";
 7         swiftPlotSeriesView1.Indicators.AddRange(new DevExpress.XtraCharts.Indicator[] {
 8         regressionLine1});
 9         series1.View = swiftPlotSeriesView1;
10         this.chartControl.SeriesSerializable = new DevExpress.XtraCharts.Series[] {
11      series1};      

  上面設定标注顯示格式,設定名稱隻要設定series的name屬性就行,不需要重新綁定,因為Legend是內建到chartControl中的,設定好後把series添加到chartControl中就可以顯示了,如果要顯示多個線性,隻要再添加series即可。

  關于刻度标示的設定:

1          swiftPlotDiagram1.AxisX.Title.Text = "時間";
2          swiftPlotDiagram1.AxisX.Title.Visible = true;            
3          swiftPlotDiagram1.AxisY.Title.Text = "記憶體大小(KB)";
4          swiftPlotDiagram1.AxisY.Title.Visible = true;      

  最後關于走勢線RegressionLine,因為是內建在chartControl,用的時候隻要訓示需要展現的series,添加到Indicators集合中就可以了,看下代碼:

1         regressionLine1.Name = "Regression Line";
 2         swiftPlotSeriesView1.Indicators.AddRange(new DevExpress.XtraCharts.Indicator[] {
 3         regressionLine1});
 4 
 5         //擷取走勢線
 6         private static RegressionLine GetRegressionLine(Series series)
 7         {
 8             if (series != null)
 9             {
10                 var swiftPlotView = series.View as SwiftPlotSeriesView;
11                 if (swiftPlotView != null)
12                 {
13                     foreach (Indicator indicator in swiftPlotView.Indicators)
14                     {
15                         var regressionLine = indicator as RegressionLine;
16                         if (regressionLine != null)
17                         {
18                             return regressionLine;
19                         }
20                     }
21                 }
22             }
23             return null;
24         }      

  說了那麼多,我們看下最後實作的效果(20毫秒,錄制的比較卡):

【實時】DevExpress記憶體監視

  完整代碼:

【實時】DevExpress記憶體監視
【實時】DevExpress記憶體監視
1 using System;
  2 using DevExpress.DXperience.Demos;
  3 using DevExpress.XtraCharts;
  4 using System.Diagnostics;
  5 
  6 namespace MemoryMonitor
  7 {
  8     public partial class ChartDemoRealtimeChart : TutorialControlBase
  9     {
 10         private const int interval = 20;
 11         private double value = 10.0;
 12         RegressionLine Regression { get { return GetRegressionLine(Series); } }
 13         private int TimeInterval
 14         {
 15             get
 16             {
 17                 return Convert.ToInt32(nud_Interval.Value);
 18             }
 19         }
 20         private Series Series
 21         {
 22             get
 23             {
 24                 return chartControl.Series.Count > 0 ? chartControl.Series[0] : null;
 25             }
 26         }
 27         #region 界面事件
 28         //執行個體化事件
 29         public ChartDemoRealtimeChart()
 30         {
 31             InitializeComponent();
 32             Regression.Visible = false;
 33             getProcess();
 34         }
 35         //timer事件
 36         private void timer_Tick(object sender, EventArgs e)
 37         {
 38             if (Series == null )
 39             {
 40                 return;
 41             }
 42             var argument = DateTime.Now;
 43             //一個刻度需要畫的坐标
 44             var pointsToUpdate = new SeriesPoint[interval];
 45             for (var i = 0; i < interval; i++)
 46             {
 47                 pointsToUpdate[i] = new SeriesPoint(argument, value);
 48                 argument = argument.AddMilliseconds(1);
 49                 UpdateValues();
 50             }
 51             //添加坐标
 52             AddPoints(Series, pointsToUpdate);
 53             var minDate = argument.AddSeconds(-TimeInterval);
 54             //重新設定X軸MinMaxValues
 55             var diagram = chartControl.Diagram as SwiftPlotDiagram;
 56             if (diagram != null && diagram.AxisX.DateTimeScaleOptions.MeasureUnit == DateTimeMeasureUnit.Millisecond)
 57             {
 58                 diagram.AxisX.Range.SetMinMaxValues(minDate, argument); 
 59             }
 60         }
 61         //開始/暫停
 62         private void btnStart_Click(object sender, EventArgs e)
 63         {
 64             timer.Enabled = !timer.Enabled;
 65             btnStart.Text = timer.Enabled ? "暫停" : "開始";
 66         }
 67         //顯示走勢線
 68         private void cb_RegressionLine_CheckedChanged(object sender, EventArgs e)
 69         {
 70             if (Regression != null)
 71                 Regression.Visible = cb_RegressionLine.Checked;
 72         }
 73         //選擇需要監控的程序
 74         private void cb_Process_SelectedIndexChanged(object sender, EventArgs e)
 75         {
 76             Series.Points.Clear();
 77         }
 78         #endregion
 79         #region Some方法
 80         //擷取下一個坐标值
 81         private double CalculateNextValue(double value)
 82         {
 83             Process process = Process.GetProcessesByName(cb_Process.Text)[0];
 84             return process.PrivateMemorySize64/1024.0;
 85         }
 86         //更新坐标值值
 87         private void UpdateValues()
 88         {
 89             value = CalculateNextValue(value);
 90         }
 91         //添加坐标
 92         private void AddPoints(Series series, SeriesPoint[] pointsToUpdate)
 93         {
 94             if (series.View is SwiftPlotSeriesViewBase)
 95             {
 96                 series.Points.AddRange(pointsToUpdate);
 97             }
 98         }
 99         //擷取走勢線
100         private static RegressionLine GetRegressionLine(Series series)
101         {
102             if (series != null)
103             {
104                 var swiftPlotView = series.View as SwiftPlotSeriesView;
105                 if (swiftPlotView != null)
106                 {
107                     foreach (Indicator indicator in swiftPlotView.Indicators)
108                     {
109                         var regressionLine = indicator as RegressionLine;
110                         if (regressionLine != null)
111                         {
112                             return regressionLine;
113                         }
114                     }
115                 }
116             }
117             return null;
118         }
119         //擷取程序
120         private void getProcess()
121         {
122             foreach (Process item in Process.GetProcesses())
123             {
124                 cb_Process.Items.Add(item.ProcessName);
125             }
126             cb_Process.SelectedIndex = 0;
127         }
128         #endregion
129     }
130 }      

View Code

  示例程式下載下傳:MemoryMonitor.rar

後記

  swiftPlotDiagram隻是chartControl的冰山一角,chartControl隻是DevExpress的冰山一角,DevExpress隻是擴充控件的冰山一角,擴充控件隻是。。。

  學無止境,不得不學。

作者:田園裡的蟋蟀

微信公衆号:你好架構

出處:http://www.cnblogs.com/xishuai/

公衆号會不定時的分享有關架構的方方面面,包含并不局限于:Microservices(微服務)、Service Mesh(服務網格)、DDD/TDD、Spring Cloud、Dubbo、Service Fabric、Linkerd、Envoy、Istio、Conduit、Kubernetes、Docker、MacOS/Linux、Java、.NET Core/ASP.NET Core、Redis、RabbitMQ、MongoDB、GitLab、CI/CD(持續內建/持續部署)、DevOps等等。

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接。

分享到:

QQ空間

新浪微網誌

騰訊微網誌

微信

更多

繼續閱讀