6

人々は、リリースされていないイベントリスナーが原因で発生するメモリリークについて話し続けています。これは非常に重要な問題だと思います。非常に深刻で非常に重要です...それが本当に存在する場合。

私は自分で問題を再現しようとしましたが、すべての試みが失敗しました:アプリケーションにメモリをリークさせることができません:(それは良い音ですが、私はまだ心配しています:何かが足りないかもしれません。

それで、誰かがメモリリークを引き起こす非常に単純なソースコードサンプルを提供できるでしょうか?

デモとして小さなVB.NETアプリケーションを作成しました。これには1つのWindowsフォームと1つのクラスが含まれています。

Windowsフォーム:コレクションオブジェクト(「c」という名前)と2つのボタンがあります。1つはコレクションに10個のアイテムを追加し、もう1つはコレクションをクリアします。

Public Class Form1

Dim c As New Collection

Private Sub btnAddItem_Click(sender As System.Object, e As System.EventArgs) Handles btnAddItem.Click
    For i As Integer = 1 To 10
        Dim m As New MyType
        c.Add(m)
    Next

    Me.Text = c.Count
End Sub

Private Sub btnClear_Click(sender As System.Object, e As System.EventArgs) Handles btnClear.Click
    For Each item As MyType In c
        item.Dispose()
    Next
    c.Clear()

    Me.Text = c.Count
End Sub
End Class

MyTypeクラス:大きなm_Imageオブジェクトがあります。これは大きいので、メモリが実際にMyTypeインスタンスによって使用されていることがわかります:)

Imports System.Drawing

Public Class MyType 
Implements IDisposable

Private m_Image As Bitmap

Public Sub New()
    AddHandler Application.Idle, AddressOf Application_Idle

    m_Image = New Bitmap(1024, 1024)
End Sub

Private Sub Application_Idle(sender As Object, e As EventArgs)

End Sub

#Region "IDisposable Support"
Private disposedValue As Boolean

Protected Overridable Sub Dispose(disposing As Boolean)
    If Not Me.disposedValue Then
        If disposing Then
            m_Image.Dispose()
        End If
    End If
    Me.disposedValue = True
End Sub

Public Sub Dispose() Implements IDisposable.Dispose
    Dispose(True)
    GC.SuppressFinalize(Me)
End Sub
#End Region

End Class
4

3 に答える 3

12

これは非常に簡単な例です

class MyType
{
    public static event EventHandler ExampleEvent;

    public MyType()
    {
        ExampleEvent += (sender, e) => OnExampleEvent();
    }
    private void OnExampleEvent() { }
}

のすべてのインスタンスがイベントMyTypeにサブスクライブしExampleEventます。このイベントは特定のオブジェクトに関連付けられていないため、メモリを離れることはありません。MyTypeこれにより、アプリケーションの期間中、 すべてのインスタンスがメモリに保持されます。

編集

コメントで求められているように、ここにあるのは、MyType使用されなくなった後もずっとメモリに残っているインスタンスのデモです。

class Program
{
    static void Main(string[] args)
    {
        WeakReference weakRef = new WeakReference(new MyType());
        for (var i = 0; i < 10; i++)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        Console.WriteLine("Still Alive: {0}", weakRef.IsAlive);
    }
}
于 2012-04-07T16:34:33.590 に答える
0

さらに調査した結果(@JaredParからの手がかりのおかげで)、次のような状況でメモリリークが発生することがわかりました。

  1. パブリックプロシージャまたは関数(プロシージャPRC1()など)を持つ新しいオブジェクトへの参照REF1を作成します。
  2. コード内の任意の場所からイベントハンドラーを追加します。任意のイベント(EVNT1など)をREF1オブジェクトのPRC1プロシージャにリンクします。
  3. REF1を削除します(VBではnullまたはNothingに設定します)。この時点から、手順1で作成したオブジェクトへの参照はありません。
  4. ただし、オブジェクトは論理的であるため、メモリに残ります。イベントが発生したときに実行されるコード(PRC1)(EVNT1)を持っています。

このような状況でメモリを解放する方法についてはアドバイスしませんが、この説明が、より優れたアーキテクチャを設計し、メモリリークを回避するのに役立つことを願っています。

于 2012-04-08T09:08:45.777 に答える
0

イベントがメモリ(およびCPU時間!)リークになる最も一般的なパターンは、オブジェクトが別のオブジェクトに何かが発生したことの通知を要求するときに発生します。これにより、短期間のオブジェクトにのみ関係する情報を更新できます。イベントサブスクリプションが存在し続けると、それを気にしていたオブジェクトがすべて放棄された場合でも、通知を行っているオブジェクトが存在し続ける限りメモリが無駄になり、オブジェクトが通知を実行するたびにCPU時間が無駄になります。 。このようなイベントサブスクリプションを無制限に作成して破棄できる場合、それらは無制限のメモリリークを構成します。

于 2012-05-04T22:17:11.830 に答える