none
Event メモリの開放について RRS feed

  • 質問

  • 以下のコードを実行すると、メモリが増えていきます。

    開放する方法が見当たらないのですが・・

    Public Class TestClass0
      Shared Sub main()
        For i As Integer = 1 To 100000
          Dim t As New TestClass1
          If i Mod 10000 = 0 Then
            Debug.WriteLine(GC.GetTotalMemory(True))
          End If
          t = Nothing
        Next
      End Sub
    End Class

    Public Class TestClass1
      Public Event TestEvent As EventHandler  ' <-- ここ
      Sub New()
      End Sub
    End Class

    2006年4月22日 11:44

回答

すべての返信

  • Nothing を代入するというのがメモリを解放するというのとイコールではありません。

    使用メモリが大きくなってきたなと感じたら、GC が自動的に既にどこからも参照しなくなったオブジェクトを片づけます。この、(その変数が)参照しなくなったと言うことを明示するのが Nothing の代入です。ちなみに、わざわざ明示しなくても(Nothing を代入しなくても)この場合ループ内に変数のスコープが限られているので、ループが終われば自動的に参照しなくなったことになります。

    ですので、メモリなど気にせず安心してコーディングしてください。

    2006年4月22日 12:32
  • えーと、すみません。
    質問の意図が明確ではありませんでした。

    TestClass1で

    Public Event TestEvent As EventHandler

    をコメントして、コードを実行し

    GC.GetTotalMemory(True)

    で、メモリ使用量を表示した場合

    431860
    490768
    490660
    490648
    490660
    490648
    490660
    490648
    490660
    490648

    となり、メモリが開放されているのが確認できますが

    Public Event TestEvent As EventHandler

    を有効にして、実行した場合

    719756
    1096920
    1163500
    1574548
    1854536
    2134512
    2196716
    2476704
    2756680
    3036668

    となり、メモリが開放されていないと思われます。
    このメモリを開放したいのですが、方法が解りません。
    どうしたらよいでしょうか。

    2006年4月24日 1:32
  • すこし.NETのガベージコレクションについて調べる必要がありそうですね。

    GCは適宜いいタイミングで開放してくれます。

    われわれ凡人が考えるよりはよりましな開放を行ってくれるはずです。

    どうしても今開放して欲しいという場合にはGC.Collect()で強制的に発動させることが可能です。

    2006年4月24日 2:12
  • 横からすみません。
    私の 環境でも girlie さんの指摘する現象が発生しました。

     中博俊 さんからの引用

    われわれ凡人が考えるよりはよりましな開放を行ってくれるはずです。

    どうしても今開放して欲しいという場合にはGC.Collect()で強制的に発動させることが可能です。

    一般論ではそうなっているはずですが、

    VB.2005 では メモリが開放されず、 同じソースでもVB.2003 ではメモリが開放されていました。
    とりあえずの回避策としては

    1.Event 宣言している クラスは無駄にインスタンス化しない。
    2.EventではなくEventHandlerを使ったデリゲートを利用する。

    などが考えてみてはいかがでしょうか?

    2006年4月24日 3:25
  • デリゲートですか。
    調べてみます。

    2006年4月24日 4:53
  • vbc.exe & CLR Debugger では再現しませんね。

    リリースビルドではどうなりますか?

    2006年4月24日 5:11
  • 確かに、Releaseビルドではメモリが増加しません。
    (上記コードのDebug.WriteLine を Console.WriteLineに変更して確認)

    ありがとうございます。

    結果:

    288136
    299672
    299672
    299672
    299672
    299672
    299672
    299672
    299672
    299672

    2006年4月24日 6:02
  • ちょっと試してみたところ、もう少しループ回数を増やしてみたりすると、たまの消費メモリが減ったりするようです。なので、確かに解放されにくいみたいですが、まったく解放されないわけではないように思います。

    どうも、クラス内に Event があるとコンパイラによって

    • Private Shared __ENCList As ArrayList というフィールドが追加される
    • 静的コンストラクタが追加される (ここで __ENCList = New ArrayList() としている)
    • コンストラクタに __ENCList.Add(New WeakReference(Me)) というコードが追加される

    といった操作が行われるようです。なんのために WeakReference で参照するようにしているのかはわかりませんが、これがあるために解放されにくくなるのかもしれません。

    ちなみに、C# の場合はこういったコードはまったく生成されないようなんですが、この __ENCList ってなんのためにあるんでしょうね。

    2006年4月24日 6:05