VB.NETでサクッとメモリ使用量を計測する
リアルタイムでCLR上のアプリのメモリ使用量なんかを確認したい!ってお話です。。。
パフォーマンスチューニングは処理速度以外にメモリ使用量なんかも対象にしますよね?
プロジェクトの後半で、長期間に渡ってテストしているとOutOfMemoryExceptionなんかで悩まされたりするはずです。
チューニングは、やはり計測が基本です。
。。ということで、今回は.NETでメモリ使用量などを手軽に計測する方法をlogります。
ポイント
- アプリのメモリ使用量の計測にMy.Application.Info.WorkingSetを使います。
- OSの空き容量の計測にDiagnostics.PerformanceCounterを使います。
- アプリのタイトルバーにメモリ使用量などを表示します。
- アドオン的に使います。
MemoryMonitorの実装&使用例
Option Explicit On Option Strict On Public Class MemoryMonitor Private _performanceCounter As Diagnostics.PerformanceCounter Private _timer As Threading.Timer Private _defaultWindowTitle As String = "" Public Sub StartMonitor(ByVal f As Form) _performanceCounter = New Diagnostics.PerformanceCounter _performanceCounter.CategoryName = "Memory" _performanceCounter.CounterName = "Available KBytes" _performanceCounter.NextValue() _defaultWindowTitle = f.Text _timer = New Threading.Timer( New Threading.TimerCallback( Sub() f.Invoke( Sub() f.Text = _defaultWindowTitle & String.Format(" [メモリ情報] 使用: {0,6:#,##0.0}MB", (My.Application.Info.WorkingSet / 1024 / 1024)) & String.Format(" 空き: {0,6:#,##0.0}MB", (_performanceCounter.NextValue() / 1024)) End Sub) End Sub), f, 1000, 1000) End Sub Public Sub StopMonitor() Try If Not _performanceCounter Is Nothing Then _performanceCounter.Dispose() _performanceCounter = Nothing End If If Not _timer Is Nothing Then _timer.Dispose() _timer = Nothing End If Catch ex As Exception Debug.Print(ex.Message) End Try End Sub End Class Partial Public Class Form1 Private mm As New MemoryMonitor Private Sub Activated_(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Me.MdiParent Is Nothing Then mm.StartMonitor(Me) End Sub Private Sub FormClosed_(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.FormClosing If Me.MdiParent Is Nothing Then mm.StopMonitor() End Sub End Class
表示したいForm(今回はForm1)のLoad、FormClosingイベントをHandleするだけです。
VSのプロジェクトに追加するだけのシンプルさです。
New Threading.Timerの第3引数で開始時間(ms)と第4引数で更新間隔(ms)を指定します。
必要に応じて、TimerCallbackのラムダでログ出力したりすると資料作りに便利ですね!
おまけ
デバッガで、タイマースレッドにステップインしちゃうとかなりウザいので、DebuggerStepThrough属性付きのパターンも挙げときます。
※ラムダはDebuggerStepThrough属性つけられないので、Delegate Subになってます。
Option Explicit On Option Strict On <Diagnostics.DebuggerStepThrough()> Public Class MemoryMonitor Private _performanceCounter As Diagnostics.PerformanceCounter Private _timer As Threading.Timer Private _defaultWindowTitle As String = "" Public Sub StartMonitor(ByVal f As Form) _performanceCounter = New Diagnostics.PerformanceCounter _performanceCounter.CategoryName = "Memory" _performanceCounter.CounterName = "Available KBytes" _performanceCounter.NextValue() _defaultWindowTitle = f.Text Dim tc As Threading.TimerCallback = AddressOf TimerCallback _timer = New Threading.Timer(tc, f, 1000, 1000) End Sub Public Sub StopMonitor() Try If Not _performanceCounter Is Nothing Then _performanceCounter.Dispose() _performanceCounter = Nothing End If If Not _timer Is Nothing Then _timer.Dispose() _timer = Nothing End If Catch ex As Exception Debug.Print(ex.Message) End Try End Sub Delegate Sub UpdateMemoryUsageDelegate(ByVal f As Form) Private Sub UpdateMemoryUsage(ByVal f As Form) f.Text = _defaultWindowTitle & String.Format(" [メモリ情報] 使用: {0,6:#,##0.0}MB", (My.Application.Info.WorkingSet / 1024 / 1024)) & String.Format(" 空き: {0,6:#,##0.0}MB", (_performanceCounter.NextValue() / 1024)) End Sub Private Sub TimerCallback(ByVal o As Object) Dim f As Form = DirectCast(o, Form) Try f.Invoke(New UpdateMemoryUsageDelegate(AddressOf UpdateMemoryUsage), New Object() {f}) Catch ex As Exception Debug.Print(ex.Message) End Try End Sub End Class Partial Public Class Form1 Private mm As New MemoryMonitor <Diagnostics.DebuggerStepThrough()> Private Sub Activated_(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Me.MdiParent Is Nothing Then mm.StartMonitor(Me) End Sub <Diagnostics.DebuggerStepThrough()> Private Sub FormClosed_(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.FormClosing If Me.MdiParent Is Nothing Then mm.StopMonitor() End Sub End Class
Work! Enjoy it!