トップ回答者
System.Printing名前空間のクラス利用時にメモリリークが発生する

質問
-
こんにちは、VB.net VS2008+WindowsXpで開発を行っています。
現在、印刷管理用のアプリケーションを作成していますが、それにあたり、プリンタのキューを監視するためにSystem.Printing名前空間のPrintServerクラスとPrintQueueクラスを利用し、利用可能なプリンタ情報の取得と、各プリンタのステータス(一時停止、トナー切れなど)を取得しています。
これまでは、Win32APIを直接利用し、プリンタのステータス情報を取得していましが、.NET Framework3.0より追加されたSystem.Printing名前空間を利用することにより、プリンタ情報の取得が容易になったこともあり、ソースの修正を行いました。
すると、これまでは発生していなかったのですが、System.Printing名前空間のPrintServerクラスのインスタンス生成、Refreshメソッドなどの特定のメソッド利用時にメモリが上昇し、解放される気配がないことに気づきました。
たとえば、画面上にボタンをひとつ配置し、そのボタンをクリックすると以下のソースを実行してみても確認できます。
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
ps = New PrintServer
End Sub
今回、動かしているアプリケーションは印刷キューを監視するための常駐プログラムのため、上記のように手動で起動するのに比べると、メソッドの実行回数も比べ物にならないため、あっという間にメモリリークしてしまいます(一定時間放置しておくと、OutOfMemory例外が実際に発生します)
PrintServerオブジェクトのDispose、Nothing代入なども行いましたが、一向に改善されません。
Gc.Collectでガベージコレクションを明示的に実行しても変化ありません。
どなたかよい回避策、対応策をご存知の方がいらっしゃいましたらご教授願います。
以下の箇所がメモリ増加の大きな原因となっていると思われます。
'-------------------------------------------------------------------------------
' 2009/07/16 Add プリンタキューの情報を返す
'
' [parameter]printername:プリンタ名
' [return] プリンタキュー(存在しない場合はNULLを返す)
'-------------------------------------------------------------------------------
Private Function GetPrinterQueueObj(ByVal printerName As String) As PrintQueue
Dim existFlg As Boolean = False
Dim queue As PrintQueue = Nothing
For Each pq As PrintQueue In _printserver.GetPrintQueues
If pq.Name = printerName Then
existFlg = True
Exit For
End If
NextIf existFlg Then
queue = _printserver.GetPrintQueue(printerName)
End If
_printserver.Dispose()
Return queue
End Function
回答
-
こんにちは
このコードを使い、100msec毎に、あるプリンタのJOBSを取得するように実行してみましたが、特にリークするような事もありませんでした。
GC.CollectionCountを見ていても、きちんとメモリの解放がされているようでした。
Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
Dim p As PrintQueue = GetPrinterQueueObj("実際のプリンタ名")
Label2.Text = DateTime.Now.Ticks.ToString & " - JOBS:" & p.NumberOfJobs
End Sub
これで、タイマ値を100ミリ秒で10分ほど動かしていましたが、特に何も起きませんでした。
_printserverを、グローバル、GetPrinterQueueObj内部、両方のスコープでやってみましたが特に問題は起きませんでした。
以下の事はやっていないので、この結果意味の無い可能性があります。
*複数のプリンタを切り替えながら、ということはやっていません。
*プリンタはネットワークプリンタで、ローカルに直接繋がっているプリンタではありません。
*思いっきり誤読してトンチンカンな事をしている可能性があります
chonmage- 回答としてマーク 高橋 春樹 2009年8月6日 2:18
すべての返信
-
こんにちは
このコードを使い、100msec毎に、あるプリンタのJOBSを取得するように実行してみましたが、特にリークするような事もありませんでした。
GC.CollectionCountを見ていても、きちんとメモリの解放がされているようでした。
Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
Dim p As PrintQueue = GetPrinterQueueObj("実際のプリンタ名")
Label2.Text = DateTime.Now.Ticks.ToString & " - JOBS:" & p.NumberOfJobs
End Sub
これで、タイマ値を100ミリ秒で10分ほど動かしていましたが、特に何も起きませんでした。
_printserverを、グローバル、GetPrinterQueueObj内部、両方のスコープでやってみましたが特に問題は起きませんでした。
以下の事はやっていないので、この結果意味の無い可能性があります。
*複数のプリンタを切り替えながら、ということはやっていません。
*プリンタはネットワークプリンタで、ローカルに直接繋がっているプリンタではありません。
*思いっきり誤読してトンチンカンな事をしている可能性があります
chonmage- 回答としてマーク 高橋 春樹 2009年8月6日 2:18