前提・実現したいこと
VB.NETにてUIソフトを作製しておりますが、メモリリークが発生してソフトが終了してしまいました。
その解決に向けて調査している中で、私には理解できない現象が発生したので教えて頂ければ幸いです。
現象
メモリリークの原因を調べる中で、Labelコントロールに時刻表示(毎秒更新)する機能の有り無しでタスクマネージャの
メモリ使用量が大きく変わることが分かりました。(モジュール①は毎秒更新、モジュール②は更新なしです)
しかし両モジュール共にGC.Collect()した後に、GC.GetTotalMemory(True)で使用量を確認すると、増加しませんでした。
各モジュールを比べると以下のような状態です。
モジュール | タスクスケジューラ | GC.GetTotalMemory(True) |
---|---|---|
①(始め) | 30.7 MB | 5365.9296875 KB |
①(1H後) | 114.6 MB | 5367.890625 KB |
②(始め) | 24.5 MB | 5379.09375 KB |
②(1H後) | 24.7 MB | 5383.109375 KB |
モジュール①: 時刻ラベルを毎秒更新
VB.NET
1 Public Sub EverySecProcess() 2 lbl_DateTime.Text = Date.Now.ToString("MM/dd HH:mm:ss") '日時ラベルの表示 3 GC.Collect() 4 GC.WaitForPendingFinalizers() 5 GC.Collect() 6 SaveMemoryLog(MethodBase.GetCurrentMethod.Name & ".SetText---End") 'メモリ量ロギング 7 End Sub
モジュール②: 時刻ラベルを更新しない
VB.NET
1 Public Sub EverySecProcess() 2 'lbl_DateTime.Text = Date.Now.ToString("MM/dd HH:mm:ss") '日時ラベルの表示 3 GC.Collect() 4 GC.WaitForPendingFinalizers() 5 GC.Collect() 6 SaveMemoryLog(MethodBase.GetCurrentMethod.Name & ".SetText---End") 'メモリ量ロギング 7 End Sub
(参考メモリ量ロギング)
VB.NET
1 Public Sub SaveMemoryLog(ByVal MethodName As String) 2 Dim fs As System.IO.FileStream = Nothing 3 4 Try 5 fs = New System.IO.FileStream(MyPath.MemoryLog, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.ReadWrite) 6 7 Using sw As New System.IO.StreamWriter(fs, enc) 8 Using tw As System.IO.TextWriter = System.IO.TextWriter.Synchronized(sw) 9 Dim MsgBuf As String = Date.Now.ToString("yyyy/MM/dd HH:mm:ss:fff") & vbTab 10 MsgBuf &= "Total Memory = " & (GC.GetTotalMemory(True) / 1024).ToString.PadRight(12, " ") & " KB" & vbTab 11 MsgBuf &= MethodName 12 13 tw.WriteLine(MsgBuf) 14 tw.Close() 15 End Using 16 sw.Close() 17 End Using 18 Catch ex As Exception 19 'ここでは何もしない 20 Finally 21 'fsがusingでは無く、finallyでcloseしているのは、swのDispose時にすでにcloseされている可能性が有るため 22 If fs IsNot Nothing Then 23 fs.Close() 24 fs.Dispose() 25 fs = Nothing 26 End If 27 End Try 28 End Sub
質問内容
-
①変更点は時刻の毎秒更新のみ(changeイベントなども組んでいない)なのですが、これがメモリリークの原因になりうるのでしょうか
-
②もし①が起こりうるのであれば、メモリリークでソフトが落ちないようにするにはどうすれば良いのでしょうか。
補足情報(FW/ツールのバージョンなど)
-
動作環境
OS: Windows 10 IoT 2016LTSB (UWF ON) -
開発環境
開発ツール: Visual Studio 2012
言語: VB.NET
対象のフレームワーク: .Net Flamework 4.5
追記① EverySecProcessの呼出元について
スタートアップフォームに作成したtmr_OneSecのTickイベントにて呼んでいます。
VB.NET
1 2 Private Sub tmr_OneSec_Tick(sender As Object, e As EventArgs) Handles tmr_OneSec.Tick 3 If MyStatus.IsInitialization Then '初期設定中はタイマー処理は実施しない 4 Exit Sub 5 End If 6 7 EverySecProcess() '毎秒処理の実行 8 9 If OneMinuteCount >= 59 Then '毎分処理の実行 10 OneMinuteCount = 0 11 EveryMinProcess() 12 Else 13 OneMinuteCount += 1 14 End If 15 End Sub 16
追記② ウィンドウメッセージの処理
大変申し訳ありません。
改めて見直したところ対象フォームのウィンドウメッセージをオーバーライドしていることが分かりました。
(デバイスの利用の為に作成したが消し忘れていたようです)
この関数をコメントアウトしたところ少し見た限りではモジュール①の状態でもリークしないようになりました。
しばらく様子を見たいと思います。
画面更新等の処理はないとの間違った情報を提示してしまい申し訳ありませんでした。
また新たな疑問として下記の処理でリークする理由(なぜLabelのtextchangeでリークするのか)についても、詳しく調べていきたいと思います。
VB.NET
1 Protected Overrides Sub WndProc(ByRef m As Message) 2 SPREAD.SuspendLayout()'別のスプレッドシートの描画停止処理 3 Try 4 Select Case m.Msg 5 ''Case WINDOW_MESSAGE._DIOEVENT 6 ''SignalInput(Integer.Parse(m.WParam.ToString)) '監視対象の機器&立ち上がり信号の場合、信号受信処理実行 7 Case Else 8 MyBase.WndProc(m) '<フォームの標準処理の実行> 9 End Select 10 Catch ex As Exception 11 SaveErrorLog(MethodBase.GetCurrentMethod.Name, ex, "m:" & m.Msg) 12 End Try 13 SPREAD.ResumeLayout() 14 End Sub
回答1件
あなたの回答
tips
プレビュー