- PR -

.NETでもメモリリーク?

投稿者投稿内容
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2002-11-15 13:08
 メモリリークと思われる現象がありました。

 ArrayListを用意します。ここにデータをnewして放り込みます。すぐにClearメソッドでクリアします。しばらく待ちます。データをnewして放り込みます。すぐにClearメソッドでクリアします。・・・以下延々と繰り返し。

 このプロセスをタスクマネージャで監視すると、メモリ使用量がどんどん増えます。1日動かし続けたところ、「何これ?」というほどメモリを使用していました。

 ガベッジコレクションがいつ働くのかわかりませんが、やはり問題ではないでしょうか。それとも、Clearメソッドではメモリは解放されていないのでしょうか。

コード:
Dim al As ArrayList = New ArrayList()
While True
    Dim i As Integer
    For i = 0 To 1000
        Dim s As String = New String("GOMI")
        al.Add(s)
    Next
    al.Clear()
    ' 待ち処理
    ' 今適当に考えているので、割愛
End While


 Services.exeがフリーズしてしまい、原因を探るためにタスクマネージャを起動すると、VB.NETでやるのが間違いといわれそうですが、VB.NETで作ったサービスのメモリ使用量がやたらと多くなるので、いろいろいじっているうちに、どうもこういうことらしいと判断しました。


 また、Excelを起動してグラフを描かせようとしたのですが、こちらでも確認できました。
コード:
Class ExcelExecute
    Dim xlApp As Excel.Application
    Public Sub New()
        xlApp = CreateObject(...)
    End Sub
    Protected Sub Finalize()
        xlApp.Quit()
    End Sub
End Class

Public Class Application
    Sub DrawGraph()
        Dim xls As ExcelExecute = New ExcelEecute()
        ' 処理いろいろ
        xls = Nothing ' これはいるの?
    End Sub


DrawGraphメソッド終了後、このアプリケーションとしてはxlsが参照しているメモリは破棄されるので、クラスExcelExecuteのFinalizeが呼ばれてExcelが落ちるだろうと期待していたのですが、20分くらい待っても落ちませんでした。ただし、アプリケーションを終了するとExcelも落ちます。また、アプリケーションは動かしたまま、エクセルだけ終了すると、以後、アプリケーションを使わずに起動するエクセルがおかしくなります。アプリケーションがエクセルを起動しようとすると例外が発生します(控えていません)。

 Finalizeはガベージコレクタが動作するときに呼ばれると、どこかに書いてあったと思いますが、では、ガベージコレクタはどうなったら『参照されていない。ゴミである』と判断するのでしょう?また、自動変数にNewでインスタンスを割り当てた場合、変数を使わなくなる前にNothingを代入してやらなければならないのでしょうか。ASP.NETの説明ではNothingがほぼ必ず代入されているのですが、「CreateObject 関数」の説明では、『最後に、参照自体を解放します。』と書いてあるにもかかわらず、コードの最後には何もありません。それともVB.NETの問題で、C#では発生しないのでしょうか。
DaikiRyuto
大ベテラン
会議室デビュー日: 2002/07/23
投稿数: 200
投稿日時: 2002-11-15 13:47
引用:

 ガベッジコレクションがいつ働くのかわかりませんが、やはり問題ではないでしょうか。それとも、Clearメソッドではメモリは解放されていないのでしょうか。



 Clearメソッドは参照をnullにするだけで、ガベコレはいつ働くかはわからないでしょう。
 
kus
会議室デビュー日: 2002/11/12
投稿数: 2
お住まい・勤務地: 北海道
投稿日時: 2002-11-15 16:54
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vbcon/html/vbconInitializationTerminationOfComponents.asp

この、リンクの説明のように、デストラクタはすぐには、働かないらしいですよ。
jack_pma
常連さん
会議室デビュー日: 2002/11/15
投稿数: 35
お住まい・勤務地: 埼玉
投稿日時: 2002-11-15 17:08
.NETは使ってないから正確な話ではないんですけども
似たようなことをしているJavaでは、確か参照をnullで潰してもそのメソッドが終了しないとガベージコレクションの対象にならない、なんて話が「Effective Java」なんかに書いてあった記憶があります。

Javaと.NETじゃ違うとは思いますが、もしかしたら同じように開放したブロックとかメソッドを抜けないとダメってことがあるかもしれません、Whileの中身をそっくり別のメソッドに移して呼び出してみた場合はどうなりますでしょうか?

もしかしたら同じような動きかも、と思っただけなんでうまくいくかは不明ですが。
DaikiRyuto
大ベテラン
会議室デビュー日: 2002/07/23
投稿数: 200
投稿日時: 2002-11-15 17:35
GC.Collectメソッドで強制的にガベージコレクションをさせて試してみるという手もありますね。
(.NETを使える環境に無いので自分で試せないのが残念)

またfinalizeメソッドは呼び出されるとは限りませんので注意。


[ メッセージ編集済み 編集者: DaikiRyuto 編集日時 2002-11-15 17:36 ]
kus
会議室デビュー日: 2002/11/12
投稿数: 2
お住まい・勤務地: 北海道
投稿日時: 2002-11-15 17:40
whileブロックの外で宣言されているものは、
whileブロック内で、clear()やnothingしても
GCの対象にはならないと思います。

whileブロックが終了してから、nothingすれば、GC対象になるような気がします。(不確定ですが。…)

あと、
>ガベージコレクタはどうなったら『参照されていない。ゴミである』と判断するのでしょう?
ですが、このリンクにかかれています。
内容は、難しくて詳しくはわかりません。(すいませんです)
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpguide/html/cpconautomaticmemorymanagement.asp
Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2002-11-15 18:51
Clear()メソッドの後に、TrimToSize()メソッドを実行
してみてはどうでしょうか。

myArrayList.Clear()
myArrayList.TrimToSize()

Happy Programming!
Akio Kasai
未記入
ぬし
会議室デビュー日: 2002/03/28
投稿数: 255
投稿日時: 2002-11-15 19:46
興味深い事例ですね.VB.NETは知りませんが,GCはLISPの世界で
古くから使われている技術であり,VB.NETだろうがJavaだろうが
所詮はLISPの真似に過ぎず,原理は同じはずです.

>ガベージコレクタはどうなったら『参照されていない。ゴミである』と判断
>するのでしょう?

「そのインスタンスが到達可能でなくなった時」です.
到達可能というのは,まあ平たく言ってそのインスタンスへの参照を
間接又は直接に持っている場合です.ですから,ある参照に上書きしたり,
その参照がスコープを抜けて無効になったりすれば,その時点でインス
タンスはゴミであると判断することが可能になります.
#以下のページが参考になるかと.
#http://www.netgene.co.jp/java/technicalTerms.html#GC

ただしGCは通常はlazyに実行されるため,ある程度メモリを圧迫する
までは回収を実行しないのが普通です.このため回収されるタイミング
は予測できません.即時回収も不可能ではないですが,それを保証する
となるとパフォーマンスを犠牲にすることになるので,通常は行いません.

GCがいつ動くかは不明だとは言え,通常はメモリを大量に消費する前には
実行されます.そうでないとGCの意味がありませんからね.メモリを大量に
消費しているにも関わらずGCが動いていないとなると,それはVMや標準
ライブラリのバグの可能性が極めて高いでしょう.

ただし,GCは正常に動いていても,参照可能であるために回収できない
という可能性もなくはないです.これについては,私はVB.NETの言語仕様
を知らないので全く分かりません.

>ですが、このリンクにかかれています。
>内容は、難しくて詳しくはわかりません。(すいませんです)
要するに「インスタンスはヒープに作られる.GCとしては世代別GCを
使ってる.到達可能なインスタンスは回収されない」という程度の,
ごく普通のことをMS地方の方言で書いてあるだけのようです.

スキルアップ/キャリアアップ(JOB@IT)